Merge "[Goldeneye] BGPLS scripts"
diff --git a/TestON/core/graph.py b/TestON/core/graph.py
new file mode 100644
index 0000000..c893adc
--- /dev/null
+++ b/TestON/core/graph.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+import time
+import random
+
+class Graph:
+    """
+    Graph class provides implementations of graph algorithms.
+    The functions currently supported include:
+    - Comparing two graphs with specified attributes for vertices and edges
+    - Getting DFI (Depth First Index) and back edges during a DFS
+    - Chain decomposition of a graph
+    - Finding (non-)cut-edges and vertices
+    """
+
+    def __init__( self ):
+        # We use a dictionary to store all information about the graph
+        self.graphDict = {}
+        # Depth-first index of each vertex
+        self.DFI = {}
+        self.currentDFI = 0
+        # Parent vertex (and edge to that vertex) of each vertex in depth-first search tree
+        self.parentVertexInDFS = {}
+        self.parentEdgeInDFS = {}
+        # Back edges of the graph generated during DFS
+        self.backEdges = {}
+        # All chains in chain decomposition algorithm
+        self.chains = []
+
+    def update( self, graphDict ):
+        """
+        Update the graph data. The current graph dictionary will be replaced by the
+        new one.
+        graphDict is in a dictionary which maps each vertex to a list of attributes.
+        An example of graphDict:
+        { vertex1: { 'edges': ..., 'name': ..., 'protocol': ... },
+          vertex2: { 'edges': ..., 'name': ..., 'protocol': ... } }
+        Each vertex should at least have an 'edges' attribute which describes the
+        adjacency information. The value of 'edges' attribute is also represented by
+        a dictionary, which maps each edge (identified by the neighbor vertex) to a
+        list of attributes.
+        An example of the edges dictionary:
+        'edges': { vertex2: { 'port': ..., 'type': ... },
+                   vertex3: { 'port': ..., 'type': ... } }
+        """
+        self.graphDict = graphDict
+        return main.TRUE
+
+    def compareGraphs( self, graphDictA, graphDictB, vertexAttributes=['edges'], edgeAttributes=['port'] ):
+        """
+        Compare two graphs.
+        By default only the adjacency relationship, i.e. 'port' attribute in
+        'edges' attribute for each vertex, is compared, To get other attributes
+        included, attribute name needs to be specified in the args, e.g.
+        vertexAttributes=[ 'edges', 'protocol' ] or
+        edgeAttributes=[ 'port', 'type' ]
+        Return main.TRUE if two graphs are equal, otherwise main.FALSE
+        """
+        try:
+            result = main.TRUE
+            for vertex in set( graphDictA ).difference( graphDictB ):
+                result = main.FALSE
+                main.log.warn( "Graph: graph B: vertex {} not found".format( vertex ) )
+            for vertex in set( graphDictB ).difference( graphDictA ):
+                result = main.FALSE
+                main.log.warn( "Graph: graph A: vertex {} not found".format( vertex ) )
+            for vertex in set( graphDictA ).intersection( graphDictB ):
+                for vertexAttribute in vertexAttributes:
+                    attributeFound = True
+                    if vertexAttribute not in graphDictA[ vertex ]:
+                        main.log.warn( "Graph: graph A -> vertex {}: attribute {} not found".format( vertex, vertexAttribute ) )
+                        attributeFound = False
+                    if vertexAttribute not in graphDictB[ vertex ]:
+                        attributeFound = False
+                        main.log.warn( "Graph: graph B -> vertex {}: attribute {} not found".format( vertex, vertexAttribute ) )
+                    if not attributeFound:
+                        result = main.FALSE
+                        continue
+                    else:
+                        # Compare two attributes
+                        attributeValueA = graphDictA[ vertex ][ vertexAttribute ]
+                        attributeValueB = graphDictB[ vertex ][ vertexAttribute ]
+                        # FIXME: the comparison may not work for (sub)attribute values that are of list type
+                        # For attributes except for 'edges', we just rely on '==' for comparison
+                        if not vertexAttribute == 'edges':
+                            if not attributeValueA == attributeValueB:
+                                result = main.FALSE
+                                main.log.warn( "Graph: vertex {}: {} does not match: {} and {}".format( vertex,
+                                                                                                        vertexAttribute,
+                                                                                                        attributeValueA,
+                                                                                                        attributeValueB ) )
+                        # The structure of 'edges' is similar to that of graphs, so we use the same method for comparison
+                        else:
+                            edgeDictA = attributeValueA
+                            edgeDictB = attributeValueB
+                            for neighbor in set( edgeDictA ).difference( edgeDictB ):
+                                result = main.FALSE
+                                main.log.warn( "Graph: graph B -> vertex {}: neighbor {} not found".format( vertex, neighbor ) )
+                            for neighbor in set( edgeDictB ).difference( edgeDictA ):
+                                result = main.FALSE
+                                main.log.warn( "Graph: graph A -> vertex {}: neighbor {} not found".format( vertex, neighbor ) )
+                            for neighbor in set( edgeDictA ).intersection( edgeDictB ):
+                                for edgeAttribute in edgeAttributes:
+                                    attributeFound = True
+                                    if edgeAttribute not in edgeDictA[ neighbor ]:
+                                        attributeFound = False
+                                        main.log.warn( "Graph: graph A -> vertex {} -> neighbor {}: attribute {} not found".format( vertex,
+                                                                                                                                    neighbor,
+                                                                                                                                    edgeAttribute ) )
+                                    if edgeAttribute not in edgeDictB[ neighbor ]:
+                                        attributeFound = False
+                                        main.log.warn( "Graph: graph B -> vertex {} -> neighbor {}: attribute {} not found".format( vertex,
+                                                                                                                                    neighbor,
+                                                                                                                                    edgeAttribute ) )
+                                    if not attributeFound:
+                                        result = main.FALSE
+                                        continue
+                                    else:
+                                        # Compare two attributes
+                                        attributeValueA = edgeDictA[ neighbor ][ edgeAttribute ]
+                                        attributeValueB = edgeDictB[ neighbor ][ edgeAttribute ]
+                                        if not attributeValueA == attributeValueB:
+                                            result = main.FALSE
+                                            main.log.warn( "Graph: vertex {} -> neighbor {}: {} does not match: {} and {}".format( vertex,
+                                                                                                                                   neighbor,
+                                                                                                                                   edgeAttribute,
+                                                                                                                                   attributeValueA,
+                                                                                                                                   attributeValueB ) )
+            if not result:
+                main.log.debug( "Graph: graphDictA: {}".format( graphDictA ) )
+                main.log.debug( "Graph: graphDictB: {}".format( graphDictB ) )
+            return result
+        except TypeError:
+            main.log.exception( "Graph: TypeError exception found" )
+            return main.ERROR
+        except KeyError:
+            main.log.exception( "Graph: KeyError exception found" )
+            return main.ERROR
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return main.ERROR
+
+    def getNonCutEdges( self ):
+        """
+        Get a list of non-cut-edges (non-bridges).
+        The definition of a cut-edge (bridge) is: the deletion of a cut-edge will
+        increase the number of connected component of a graph.
+        The function is realized by impelementing Schmidt's algorithm based on
+        chain decomposition.
+        Returns a list of edges, e.g.
+        [ [ vertex1, vertex2 ], [ vertex2, vertex3 ] ]
+        """
+        try:
+            if not self.depthFirstSearch():
+                return None
+            if not self.findChains():
+                return None
+            nonCutEdges = []
+            for chain in self.chains:
+                for edge in chain:
+                    nonCutEdges.append( edge )
+            main.log.debug( 'Non-cut-edges: {}'.format( nonCutEdges ) )
+            return nonCutEdges
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return None
+
+    def getNonCutVertices( self ):
+        """
+        Get a list of non-cut-vertices.
+        The definition of a cut-vertex is: the deletion of a cut-vertex will
+        increase the number of connected component of a graph.
+        The function is realized by impelementing Schmidt's algorithm based on
+        chain decomposition.
+        Returns a list of vertices, e.g. [ vertex1, vertex2, vertex3 ]
+        """
+        try:
+            nonCutEdges = self.getNonCutEdges()
+            # find all cycle chains
+            cycleChains = []
+            for chain in self.chains:
+                # if the source vertex of the first chain equals to the destination vertex of the last
+                # chain, the chain is a cycle chain
+                if chain[ 0 ][ 0 ] == chain[ -1 ][ 1 ]:
+                    cycleChains.append( chain )
+            main.log.debug( 'Cycle chains: {}'.format( cycleChains ) )
+            # Get a set of vertices which are the first vertices of a cycle chain (excluding the first
+            # cycle chain), and these vertices are a subset of all cut-vertices
+            subsetOfCutVertices = []
+            if len( cycleChains ) > 1:
+                for cycleChain in cycleChains[ 1: ]:
+                    subsetOfCutVertices.append( cycleChain[ 0 ][ 0 ] )
+            main.log.debug( 'Subset of cut vertices: {}'.format( subsetOfCutVertices ) )
+            nonCutVertices = []
+            assert nonCutEdges != None
+            for vertex in self.graphDict.keys():
+                if vertex in subsetOfCutVertices:
+                    continue
+                vertexIsNonCut = True
+                for neighbor in self.graphDict[ vertex ][ 'edges' ].keys():
+                    edge = [ vertex, neighbor ]
+                    backwardEdge = [ neighbor, vertex ]
+                    if not edge in nonCutEdges and not backwardEdge in nonCutEdges:
+                        vertexIsNonCut = False
+                        break
+                if vertexIsNonCut:
+                    nonCutVertices.append( vertex )
+            main.log.debug( 'Non-cut-vertices: {}'.format( nonCutVertices ) )
+            return nonCutVertices
+        except KeyError:
+            main.log.exception( "Graph: KeyError exception found" )
+            return None
+        except AssertionError:
+            main.log.exception( "Graph: AssertionError exception found" )
+            return None
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return None
+
+    def depthFirstSearch( self ):
+        """
+        This function runs a depth-first search and gets DFI of each vertex as well
+        as generates the back edges
+        """
+        try:
+            assert self.graphDict != None and len( self.graphDict ) != 0
+            for vertex in self.graphDict.keys():
+                self.DFI[ vertex ] = -1
+                self.parentVertexInDFS[ vertex ] = ''
+                self.parentEdgeInDFS[ vertex ] = None
+            firstVertex = self.graphDict.keys()[ 0 ]
+            self.currentDFI = 0
+            self.backEdges = {}
+            if not self.depthFirstSearchRecursive( firstVertex ):
+                return main.ERROR
+            return main.TRUE
+        except KeyError:
+            main.log.exception( "Graph: KeyError exception found" )
+            return main.ERROR
+        except AssertionError:
+            main.log.exception( "Graph: AssertionError exception found" )
+            return main.ERROR
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return main.ERROR
+
+    def depthFirstSearchRecursive( self, vertex ):
+        """
+        Recursive function for depth-first search
+        """
+        try:
+            self.DFI[ vertex ] = self.currentDFI
+            self.currentDFI += 1
+            for neighbor in self.graphDict[ vertex ][ 'edges' ].keys():
+                edge = [ vertex, neighbor ]
+                backwardEdge = [ neighbor, vertex ]
+                if neighbor == self.parentVertexInDFS[ vertex ]:
+                    continue
+                elif self.DFI[ neighbor ] == -1:
+                    self.parentVertexInDFS[ neighbor ] = vertex
+                    self.parentEdgeInDFS[ neighbor ] = backwardEdge
+                    if not self.depthFirstSearchRecursive( neighbor ):
+                        return main.ERROR
+                else:
+                    key = self.DFI[ neighbor ]
+                    if key in self.backEdges.keys():
+                        if not edge in self.backEdges[ key ] and\
+                        not backwardEdge in self.backEdges[ key ]:
+                            self.backEdges[ key ].append( backwardEdge )
+                    else:
+                        tempKey = self.DFI[ vertex ]
+                        if tempKey in self.backEdges.keys():
+                            if not edge in self.backEdges[ tempKey ] and\
+                            not backwardEdge in self.backEdges[ tempKey ]:
+                                self.backEdges[ key ] = [ backwardEdge ]
+                        else:
+                            self.backEdges[ key ] = [ backwardEdge ]
+            return main.TRUE
+        except KeyError:
+            main.log.exception( "Graph: KeyError exception found" )
+            return main.ERROR
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return main.ERROR
+
+    def findChains( self ):
+        """
+        This function finds all the chains in chain-decomposition algorithm
+        """
+        keyList = self.backEdges.keys()
+        keyList.sort()
+        vertexIsVisited = {}
+        self.chains = []
+        for vertex in self.graphDict.keys():
+            vertexIsVisited[ vertex ] = False
+        try:
+            for key in keyList:
+                backEdgeList = self.backEdges[ key ]
+                for edge in backEdgeList:
+                    chain = []
+                    currentEdge = edge
+                    sourceVertex = edge[ 0 ]
+                    while True:
+                        currentVertex = currentEdge[ 0 ]
+                        nextVertex = currentEdge[ 1 ]
+                        vertexIsVisited[ currentVertex ] = True
+                        chain.append( currentEdge )
+                        if nextVertex == sourceVertex or vertexIsVisited[ nextVertex ] == True:
+                            break
+                        currentEdge = self.parentEdgeInDFS[ nextVertex ]
+                    self.chains.append( chain )
+            return main.TRUE
+        except KeyError:
+            main.log.exception( "Graph: KeyError exception found" )
+            return main.ERROR
+        except Exception:
+            main.log.exception( "Graph: Uncaught exception" )
+            return main.ERROR
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index ebccce0..08f242d 100644
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -75,7 +75,7 @@
             main.log.exception( "Error parsing jsonObject" )
             return None
 
-    def send( self, ip, port, url, base="/onos/v1", method="GET",
+    def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
               query=None, data=None, debug=False ):
         """
         Arguments:
@@ -94,6 +94,14 @@
         # TODO: should we maybe just pass kwargs straight to response?
         # TODO: Do we need to allow for other protocols besides http?
         # ANSWER: Not yet, but potentially https with certificates
+        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
+
         try:
             path = "http://" + str( ip ) + ":" + str( port ) + base + url
             if self.user_name and self.pwd:
@@ -137,7 +145,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/intents" )
+            response = self.send( url="/intents", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -185,7 +193,7 @@
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
             query = "/" + str( appId ) + "/" + str( intentId )
-            response = self.send( ip, port, url="/intents" + query )
+            response = self.send( url="/intents" + query, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -248,7 +256,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/applications" )
+            response = self.send( url="/applications", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -291,8 +299,9 @@
                                "from topo file" )
                 port = self.port
             query = "/" + str( appName ) + "/active"
-            response = self.send( ip, port, method="POST",
-                                  url="/applications" + query )
+            response = self.send( method="POST",
+                                  url="/applications" + query,
+                                  ip = ip, port = port)
             if response:
                 output = response[ 1 ]
                 app = json.loads( output )
@@ -347,8 +356,9 @@
                                "from topo file" )
                 port = self.port
             query = "/" + str( appName ) + "/active"
-            response = self.send( ip, port, method="DELETE",
-                                  url="/applications" + query )
+            response = self.send( method="DELETE",
+                                  url="/applications" + query,
+                                  ip = ip, port = port )
             if response:
                 output = response[ 1 ]
                 app = json.loads( output )
@@ -401,7 +411,8 @@
                                "from topo file" )
                 port = self.port
             query = "/" + project + str( appName )
-            response = self.send( ip, port, url="/applications" + query )
+            response = self.send( url="/applications" + query,
+                                  ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -420,7 +431,7 @@
             main.exit()
 
     def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
-                       ip="DEFAULT", port="DEFAULT" ):
+                       ip="DEFAULT", port="DEFAULT", vlanId="" ):
         """
         Description:
             Adds a host-to-host intent ( bidirectional ) by
@@ -443,6 +454,9 @@
                           "constraints": [{"type": "LinkTypeConstraint",
                                            "types": ["OPTICAL"],
                                            "inclusive": 'false' }]}
+            if vlanId:
+                intentJson[ 'selector' ][ 'criteria' ].append( { "type":"VLAN_VID",
+                                                                 "vlanId":vlanId } )
             output = None
             if ip == "DEFAULT":
                 main.log.warn( "No ip given, reverting to ip from topo file" )
@@ -451,10 +465,8 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip,
-                                  port,
-                                  method="POST",
-                                  url="/intents",
+            response = self.send( method="POST",
+                                  url="/intents", ip = ip, port = port,
                                   data=json.dumps( intentJson ) )
             if response:
                 if 201:
@@ -492,7 +504,8 @@
                         tcpSrc="",
                         tcpDst="",
                         ip="DEFAULT",
-                        port="DEFAULT" ):
+                        port="DEFAULT",
+                        vlanId="" ):
         """
         Description:
             Adds a point-to-point intent ( uni-directional ) by
@@ -590,6 +603,10 @@
                 intentJson[ 'selector' ][ 'criteria' ].append(
                                                        { "type":"IP_PROTO",
                                                          "protocol": ipProto } )
+            if vlanId:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"VLAN_VID",
+                                                         "vlanId": vlanId } )
 
             # TODO: Bandwidth and Lambda will be implemented if needed
 
@@ -603,10 +620,8 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip,
-                                  port,
-                                  method="POST",
-                                  url="/intents",
+            response = self.send( method="POST",
+                                  url="/intents", ip = ip, port = port,
                                   data=json.dumps( intentJson ) )
             if response:
                 if 201:
@@ -644,10 +659,8 @@
                 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 )
+            response = self.send( method="DELETE",
+                                  url="/intents" + query, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
@@ -747,7 +760,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/hosts" )
+            response = self.send( url="/hosts", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -793,7 +806,7 @@
                                "from topo file" )
                 port = self.port
             query = "/" + mac + "/" + vlan
-            response = self.send( ip, port, url="/hosts" + query )
+            response = self.send( url="/hosts" + query, ip = ip, port = port )
             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?
@@ -832,7 +845,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/topology" )
+            response = self.send( url="/topology", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -869,7 +882,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/devices" )
+            response = self.send( url="/devices", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -1032,7 +1045,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            response = self.send( ip, port, url="/flows" )
+            response = self.send( url="/flows", ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -1075,7 +1088,7 @@
             if flowId:
                 url += "/" + str( int( flowId ) )
             print url
-            response = self.send( ip, port, url=url )
+            response = self.send( url=url, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -1123,10 +1136,8 @@
                                "from topo file" )
                 port = self.port
             url = "/flows/" + deviceId
-            response = self.send( ip,
-                                  port,
-                                  method="POST",
-                                  url=url,
+            response = self.send( method="POST",
+                                  url=url, ip = ip, port = port,
                                   data=json.dumps( flowJson ) )
             if response:
                 if 201:
@@ -1292,10 +1303,8 @@
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
             query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
-            response = self.send( ip,
-                                  port,
-                                  method="DELETE",
-                                  url="/flows" + query )
+            response = self.send( method="DELETE",
+                                  url="/flows" + query, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
@@ -1365,7 +1374,7 @@
                     url += "/" + subjectKey
                     if configKey:
                         url += "/" + configKey
-            response = self.send( ip, port, url=url )
+            response = self.send( url=url, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
@@ -1415,9 +1424,8 @@
                     url += "/" + subjectKey
                     if configKey:
                         url += "/" + configKey
-            response = self.send( ip, port,
-                                  method="POST",
-                                  url=url,
+            response = self.send( method="POST",
+                                  url=url, ip = ip, port = port,
                                   data=json.dumps( cfgJson ) )
             if response:
                 if 200 <= response[ 0 ] <= 299:
@@ -1462,9 +1470,8 @@
                     url += "/" + subjectKey
                     if configKey:
                         url += "/" + configKey
-            response = self.send( ip, port,
-                                  method="DELETE",
-                                  url=url )
+            response = self.send( method="DELETE",
+                                  url=url, ip = ip, port = port )
             if response:
                 if 200 <= response[ 0 ] <= 299:
                     main.log.info( self.name + ": Successfully delete cfg" )
@@ -1667,10 +1674,8 @@
                                "from topo file" )
                 port = self.port
             url = "/flows/"
-            response = self.send( ip,
-                                  port,
-                                  method="POST",
-                                  url=url,
+            response = self.send( method="POST",
+                                  url=url, ip = ip, port = port,
                                   data=json.dumps( batch ) )
             #main.log.info("Post response is: ", str(response[0]))
             if response[0] == 200:
@@ -1712,10 +1717,8 @@
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
 
-            response = self.send( ip,
-                                  port,
-                                  method="DELETE",
-                                  url="/flows/",
+            response = self.send( method="DELETE",
+                                  url="/flows/", ip = ip, port = port,
                                   data = json.dumps(batch) )
             if response:
                 if 200 <= response[ 0 ] <= 299:
@@ -1731,3 +1734,91 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+
+    def getTopology( self, topologyOutput ):
+        """
+        Definition:
+            Loads a json topology output
+        Return:
+            topology = current ONOS topology
+        """
+        import json
+        try:
+            # either onos:topology or 'topology' will work in CLI
+            topology = json.loads(topologyOutput)
+            main.log.debug( topology )
+            return topology
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def checkStatus(
+            self,
+            topologyResult,
+            numoswitch,
+            numolink,
+            logLevel="info" ):
+        """
+        Checks the number of switches & links that ONOS sees against the
+        supplied values. By default this will report to main.log, but the
+        log level can be specific.
+
+        Params: topologyResult = the output of topology command
+                numoswitch = expected number of switches
+                numolink = expected number of links
+                logLevel = level to log to.
+                Currently accepts 'info', 'warn' and 'report'
+
+        Returns: main.TRUE if the number of switches and links are correct,
+                 main.FALSE if the number of switches and links is incorrect,
+                 and main.ERROR otherwise
+        """
+        try:
+            topology = self.getTopology( topologyResult )
+            if topology == {}:
+                return main.ERROR
+            output = ""
+            # Is the number of switches is what we expected
+            devices = topology.get( 'devices', False )
+            links = topology.get( 'links', False )
+            if devices is False or links is False:
+                return main.ERROR
+            switchCheck = ( int( devices ) == int( numoswitch ) )
+            # Is the number of links is what we expected
+            linkCheck = ( int( links ) == int( numolink ) )
+            if switchCheck and linkCheck:
+                # We expected the correct numbers
+                output = output + "The number of links and switches match "\
+                    + "what was expected"
+                result = main.TRUE
+            else:
+                output = output + \
+                    "The number of links and switches does not match " + \
+                    "what was expected"
+                result = main.FALSE
+            output = output + "\n ONOS sees %i devices" % int( devices )
+            output = output + " (%i expected) " % int( numoswitch )
+            output = output + "and %i links " % int( links )
+            output = output + "(%i expected)" % int( numolink )
+            if logLevel == "report":
+                main.log.report( output )
+            elif logLevel == "warn":
+                main.log.warn( output )
+            else:
+                main.log.info( output )
+            return result
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
\ No newline at end of file
diff --git a/TestON/drivers/common/api/dockerapidriver.py b/TestON/drivers/common/api/dockerapidriver.py
index f8e9e23..4a874c6 100644
--- a/TestON/drivers/common/api/dockerapidriver.py
+++ b/TestON/drivers/common/api/dockerapidriver.py
@@ -242,14 +242,16 @@
             main.cleanup()
             main.exit()
 
-    def onosFormCluster( self, onosIPs, cmdPath="~/OnosSystemTest/TestON/tests/PLATdockertest/Dependency", user="karaf", passwd="karaf" ):
+    def onosFormCluster( self, onosIPs, cmdPath, user="karaf", passwd="karaf" ):
         """
             From ONOS cluster for IP addresses in onosIPs list
         """
         try:
             onosIPs = " ".join(onosIPs)
-            command = cmdPath + "/onos-form-cluster -u " + user + " -p " + passwd + \
-                    " " + onosIPs
+            command = "{}/onos-form-cluster -u {} -p {} {}".format( cmdPath,
+                                                                    user,
+                                                                    passwd,
+                                                                    onosIPs )
             result = subprocess.call( command, shell=True )
             if result == 0:
                 return main.TRUE
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 5a165a2..b38e257 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -617,6 +617,60 @@
             main.cleanup()
             main.exit()
 
+    def pingHostSetAlternative( self, dstIPList, wait=1, IPv6=False ):
+        """
+        Description:
+            Ping a set of destination host from host CLI.
+            Logging into a Mininet host CLI is required before calling this funtion.
+        Params:
+            dstIPList is a list of destination ip addresses
+        Returns:
+            main.TRUE if the destination host is reachable
+            main.FALSE otherwise
+        """
+        isReachable = main.TRUE
+        wait = int( wait )
+        cmd = "ping"
+        if IPv6:
+            cmd = cmd + "6"
+        cmd = cmd + " -c 1 -i 1 -W " + str( wait )
+        try:
+            for dstIP in dstIPList:
+                pingCmd = cmd + " " + dstIP
+                self.handle.sendline( pingCmd )
+                i = self.handle.expect( [ self.hostPrompt,
+                                          '\*\*\* Unknown command: ' + pingCmd,
+                                          pexpect.TIMEOUT ],
+                                        timeout=wait + 1 )
+                if i == 0:
+                    response = self.handle.before
+                    if not re.search( ',\s0\%\spacket\sloss', response ):
+                        main.log.debug( "Ping failed between %s and %s" % ( self.name, dstIP ) )
+                        isReachable = main.FALSE
+                elif i == 1:
+                    main.log.error( self.name + ": function should be called from host CLI instead of Mininet CLI" )
+                    main.cleanup()
+                    main.exit()
+                elif i == 2:
+                    main.log.error( self.name + ": timeout when waiting for response" )
+                    isReachable = main.FALSE
+                else:
+                    main.log.error( self.name + ": unknown response: " + self.handle.before )
+                    isReachable = main.FALSE
+        except pexpect.TIMEOUT:
+            main.log.exception( self.name + ": TIMEOUT exception" )
+            isReachable = main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+        return isReachable
+
     def checkIP( self, host ):
         """
            Verifies the host's ip configured or not."""
@@ -2075,6 +2129,8 @@
     def flowTableComp( self, flowTable1, flowTable2 ):
         # This function compares the selctors and treatments of each flow
         try:
+            assert flowTable1, "flowTable1 is empty or None"
+            assert flowTable2, "flowTable2 is empty or None"
             returnValue = main.TRUE
             if len(flowTable1) != len(flowTable2):
                 main.log.warn( "Flow table lengths do not match" )
@@ -2097,6 +2153,9 @@
                     returnValue = main.FALSE
                     break
             return returnValue
+        except AssertionError:
+            main.log.exception( "Nothing to compare" )
+            return main.FALSE
         except Exception:
             main.log.exception( "Uncaught exception!" )
             main.cleanup()
@@ -2115,71 +2174,80 @@
         returns: A list of flows in json format
         '''
         jsonFlowTable = []
-        for flow in flowTable:
-            jsonFlow = {}
-            # split up the fields of the flow
-            parsedFlow = flow.split(", ")
-            # get rid of any spaces in front of the field
-            for i in range( len(parsedFlow) ):
-                item = parsedFlow[i]
-                if item[0] == " ":
-                    parsedFlow[i] = item[1:]
-            # grab the selector and treatment from the parsed flow
-            # the last element is the selector and the treatment
-            temp = parsedFlow.pop(-1)
-            # split up the selector and the treatment
-            temp = temp.split(" ")
-            index = 0
-            # parse the flags
-            # NOTE: This only parses one flag
-            flag = {}
-            if version == "1.3":
-                flag = {"flag":[temp[index]]}
+        try:
+            for flow in flowTable:
+                jsonFlow = {}
+                # split up the fields of the flow
+                parsedFlow = flow.split(", ")
+                # get rid of any spaces in front of the field
+                for i in range( len(parsedFlow) ):
+                    item = parsedFlow[i]
+                    if item[0] == " ":
+                        parsedFlow[i] = item[1:]
+                # grab the selector and treatment from the parsed flow
+                # the last element is the selector and the treatment
+                temp = parsedFlow.pop(-1)
+                # split up the selector and the treatment
+                temp = temp.split(" ")
+                index = 0
+                # parse the flags
+                # NOTE: This only parses one flag
+                flag = {}
+                if version == "1.3":
+                    flag = {"flag":[temp[index]]}
+                    index += 1
+                # the first element is the selector and split it up
+                sel = temp[index]
                 index += 1
-            # the first element is the selector and split it up
-            sel = temp[index]
-            index += 1
-            sel = sel.split(",")
-            # the priority is stuck in the selecter so put it back
-            # in the flow
-            parsedFlow.append(sel.pop(0))
-            # parse selector
-            criteria = []
-            for item in sel:
-                # this is the type of the packet e.g. "arp"
-                if "=" not in item:
-                    criteria.append( {"type":item} )
-                else:
+                sel = sel.split(",")
+                # the priority is stuck in the selecter so put it back
+                # in the flow
+                parsedFlow.append(sel.pop(0))
+                # parse selector
+                criteria = []
+                for item in sel:
+                    # this is the type of the packet e.g. "arp"
+                    if "=" not in item:
+                        criteria.append( {"type":item} )
+                    else:
+                        field = item.split("=")
+                        criteria.append( {field[0]:field[1]} )
+                selector = {"selector": {"criteria":sorted(criteria)} }
+                treat = temp[index]
+                # get rid of the action part e.g. "action=output:2"
+                # we will add it back later
+                treat = treat.split("=")
+                treat.pop(0)
+                # parse treatment
+                action = []
+                for item in treat:
+                    field = item.split(":")
+                    action.append( {field[0]:field[1]} )
+                # create the treatment field and add the actions
+                treatment = {"treatment": {"action":sorted(action)} }
+                # parse the rest of the flow
+                for item in parsedFlow:
                     field = item.split("=")
-                    criteria.append( {field[0]:field[1]} )
-            selector = {"selector": {"criteria":sorted(criteria)} }
-            treat = temp[index]
-            # get rid of the action part e.g. "action=output:2"
-            # we will add it back later
-            treat = treat.split("=")
-            treat.pop(0)
-            # parse treatment
-            action = []
-            for item in treat:
-                field = item.split(":")
-                action.append( {field[0]:field[1]} )
-            # create the treatment field and add the actions
-            treatment = {"treatment": {"action":sorted(action)} }
-            # parse the rest of the flow
-            for item in parsedFlow:
-                field = item.split("=")
-                jsonFlow.update( {field[0]:field[1]} )
-            # add the treatment and the selector to the json flow
-            jsonFlow.update( selector )
-            jsonFlow.update( treatment )
-            jsonFlow.update( flag )
+                    jsonFlow.update( {field[0]:field[1]} )
+                # add the treatment and the selector to the json flow
+                jsonFlow.update( selector )
+                jsonFlow.update( treatment )
+                jsonFlow.update( flag )
 
-            if debug: main.log.debug( "\033[94mJson flow:\033[0m\n{}\n".format(jsonFlow) )
+                if debug: main.log.debug( "\033[94mJson flow:\033[0m\n{}\n".format(jsonFlow) )
 
-            # add the json flow to the json flow table
-            jsonFlowTable.append( jsonFlow )
+                # add the json flow to the json flow table
+                jsonFlowTable.append( jsonFlow )
 
-        return jsonFlowTable
+            return jsonFlowTable
+
+        except IndexError:
+            main.log.exception( self.name + ": IndexError found" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getFlowTable( self, sw, version="", debug=False):
         '''
@@ -2253,6 +2321,8 @@
         try:
             main.log.info( "Getting flows from Mininet" )
             flows = self.getFlowTable( sw, version, debug )
+            if flows == None:
+                return main.ERROR
 
             if debug: print "flow ids:\n{}\n\n".format(flowId)
 
@@ -3050,624 +3120,6 @@
             main.cleanup()
             main.exit()
 
-    def startScapy( self, mplsPath="" ):
-        """
-        Start the Scapy cli
-        optional:
-            mplsPath - The path where the MPLS class is located
-            NOTE: This can be a relative path from the user's home dir
-        """
-        mplsLines = ['import imp',
-            'imp.load_source( "mplsClass", "{}mplsClass.py" )'.format(mplsPath),
-            'from mplsClass import MPLS',
-            'bind_layers(Ether, MPLS, type = 0x8847)',
-            'bind_layers(MPLS, MPLS, bottom_of_label_stack = 0)',
-            'bind_layers(MPLS, IP)']
-
-        try:
-            self.handle.sendline( "scapy" )
-            self.handle.expect( self.scapyPrompt )
-            self.handle.sendline( "conf.color_theme = NoTheme()" )
-            self.handle.expect( self.scapyPrompt )
-            if mplsPath:
-                main.log.info( "Adding MPLS class" )
-                main.log.info( "MPLS class path: " + mplsPath )
-                for line in mplsLines:
-                    main.log.info( "sending line: " + line )
-                    self.handle.sendline( line )
-                    self.handle.expect( self.scapyPrompt )
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def stopScapy( self ):
-        """
-        Exit the Scapy cli
-        """
-        try:
-            self.handle.sendline( "exit()" )
-            self.handle.expect( self.hostPrompt )
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildEther( self, **kwargs ):
-        """
-        Build an Ethernet frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-        Default frame:
-        ###[ Ethernet ]###
-          dst= ff:ff:ff:ff:ff:ff
-          src= 00:00:00:00:00:00
-          type= 0x800
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the Ethernet frame
-            cmd = 'ether = Ether( '
-            options = []
-            for key, value in kwargs.iteritems():
-                if isinstance( value, str ):
-                    value = '"' + value + '"'
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            self.handle.sendline( "packet = ether" )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildIP( self, **kwargs ):
-        """
-        Build an IP frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-        Default frame:
-        ###[ IP ]###
-          version= 4
-          ihl= None
-          tos= 0x0
-          len= None
-          id= 1
-          flags=
-          frag= 0
-          ttl= 64
-          proto= hopopt
-          chksum= None
-          src= 127.0.0.1
-          dst= 127.0.0.1
-          \options\
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the IP frame
-            cmd = 'ip = IP( '
-            options = []
-            for key, value in kwargs.iteritems():
-                if isinstance( value, str ):
-                    value = '"' + value + '"'
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            self.handle.sendline( "packet = ether/ip" )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildIPv6( self, **kwargs ):
-        """
-        Build an IPv6 frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-        Default frame:
-        ###[ IPv6 ]###
-          version= 6
-          tc= 0
-          fl= 0
-          plen= None
-          nh= No Next Header
-          hlim= 64
-          src= ::1
-          dst= ::1
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the IPv6 frame
-            cmd = 'ipv6 = IPv6( '
-            options = []
-            for key, value in kwargs.iteritems():
-                if isinstance( value, str ):
-                    value = '"' + value + '"'
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            self.handle.sendline( "packet = ether/ipv6" )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildTCP( self, ipVersion=4, **kwargs ):
-        """
-        Build an TCP frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-
-        NOTE: Some arguments require quotes around them. It's up to you to
-        know which ones and to add them yourself. Arguments with an asterisk
-        do not need quotes.
-
-        Options:
-        ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
-                    frame to use to encapsulate into
-        Default frame:
-        ###[ TCP ]###
-          sport= ftp_data *
-          dport= http *
-          seq= 0
-          ack= 0
-          dataofs= None
-          reserved= 0
-          flags= S
-          window= 8192
-          chksum= None
-          urgptr= 0
-          options= {}
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the TCP frame
-            cmd = 'tcp = TCP( '
-            options = []
-            for key, value in kwargs.iteritems():
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            if str( ipVersion ) is '4':
-                self.handle.sendline( "packet = ether/ip/tcp" )
-            elif str( ipVersion ) is '6':
-                self.handle.sendline( "packet = ether/ipv6/tcp" )
-            else:
-                main.log.error( "Unrecognized option for ipVersion, given " +
-                                repr( ipVersion ) )
-                return main.FALSE
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildUDP( self, ipVersion=4, **kwargs ):
-        """
-        Build an UDP frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-
-        NOTE: Some arguments require quotes around them. It's up to you to
-        know which ones and to add them yourself. Arguments with an asterisk
-        do not need quotes.
-
-        Options:
-        ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
-                    frame to use to encapsulate into
-        Default frame:
-        ###[ UDP ]###
-          sport= domain *
-          dport= domain *
-          len= None
-          chksum= None
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the UDP frame
-            cmd = 'udp = UDP( '
-            options = []
-            for key, value in kwargs.iteritems():
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            if str( ipVersion ) is '4':
-                self.handle.sendline( "packet = ether/ip/udp" )
-            elif str( ipVersion ) is '6':
-                self.handle.sendline( "packet = ether/ipv6/udp" )
-            else:
-                main.log.error( "Unrecognized option for ipVersion, given " +
-                                repr( ipVersion ) )
-                return main.FALSE
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def buildICMP( self, **kwargs ):
-        """
-        Build an ICMP frame
-
-        Will create a frame class with the given options. If a field is
-        left blank it will default to the below value unless it is
-        overwritten by the next frame.
-        Default frame:
-        ###[ ICMP ]###
-          type= echo-request
-          code= 0
-          chksum= None
-          id= 0x0
-          seq= 0x0
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # Set the ICMP frame
-            cmd = 'icmp = ICMP( '
-            options = []
-            for key, value in kwargs.iteritems():
-                if isinstance( value, str ):
-                    value = '"' + value + '"'
-                options.append( str( key ) + "=" + str( value ) )
-            cmd += ", ".join( options )
-            cmd += ' )'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            self.handle.sendline( "packet = ether/ip/icmp" )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def sendPacket( self, iface=None, packet=None, timeout=1 ):
-        """
-        Send a packet with either the given scapy packet command, or use the
-        packet saved in the variable 'packet'.
-
-        Examples of a valid string for packet:
-
-        Simple IP packet
-        packet='Ether(dst="a6:d9:26:df:1d:4b")/IP(dst="10.0.0.2")'
-
-        A Ping with two vlan tags
-        packet='Ether(dst='ff:ff:ff:ff:ff:ff')/Dot1Q(vlan=1)/Dot1Q(vlan=10)/
-                IP(dst='255.255.255.255', src='192.168.0.1')/ICMP()'
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # TODO: add all params, or use kwargs
-            sendCmd = 'srp( '
-            if packet:
-                sendCmd += packet
-            else:
-                sendCmd += "packet"
-            if iface:
-                sendCmd += ", iface='{}'".format( iface )
-
-            sendCmd += ', timeout=' + str( timeout ) + ')'
-            self.handle.sendline( sendCmd )
-            self.handle.expect( self.scapyPrompt )
-            if "Traceback" in self.handle.before:
-                # KeyError, SyntaxError, ...
-                main.log.error( "Error in sending command: " + self.handle.before )
-                return main.FALSE
-            # TODO: Check # of packets sent?
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def startFilter( self, ifaceName=None, sniffCount=1, pktFilter="ip" ):
-        """
-        Listen for packets using the given filters
-
-        Options:
-        ifaceName - the name of the interface to listen on. If none is given,
-                    defaults to <host name>-eth0
-        pktFilter - A string in Berkeley Packet Filter (BPF) format which
-                    specifies which packets to sniff
-        sniffCount - The number of matching packets to capture before returning
-
-        Returns main.TRUE or main.FALSE on error
-        """
-        try:
-            # TODO: add all params, or use kwargs
-            ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
-            # Set interface
-            self.handle.sendline( ' conf.iface = "' + ifaceName + '"' )
-            self.handle.expect( self.scapyPrompt )
-            cmd = 'pkt = sniff(count = ' + str( sniffCount ) +\
-                  ', filter = "' + str( pktFilter ) + '")'
-            self.handle.sendline( cmd )
-            self.handle.expect( '"\)\r\n' )
-            # TODO: parse this?
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def checkFilter( self ):
-        """
-        Check that a filter returned and returns the reponse
-        """
-        try:
-            i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ] )
-            if i == 0:
-                return main.TRUE
-            else:
-                return main.FALSE
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def killFilter( self ):
-        """
-        Kill a scapy filter
-        """
-        try:
-            self.handle.send( "\x03" )  # Send a ctrl-c to kill the filter
-            self.handle.expect( self.scapyPrompt )
-            return self.handle.before
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return None
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def readPackets( self ):
-        """
-        Read all the packets captured by the previous filter
-        """
-        try:
-            self.handle.sendline( "for p in pkt: p \n")
-            self.handle.expect( "for p in pkt: p \r\n... \r\n" )
-            self.handle.expect( self.scapyPrompt )
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return None
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-        return self.handle.before
-
-    def updateSelf( self ):
-        """
-        Updates local MAC and IP fields
-        """
-        self.hostMac = self.getMac()
-        self.hostIp = self.getIp()
-
-    def getMac( self, ifaceName=None ):
-        """
-        Save host's MAC address
-        """
-        try:
-            ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
-            cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-            pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
-            match = re.search( pattern, self.handle.before )
-            if match:
-                return match.group()
-            else:
-                # the command will have an exception if iface doesn't exist
-                return None
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return None
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def getIp( self, ifaceName=None ):
-        """
-        Save host's IP address
-        """
-        try:
-            ifaceName = ifaceName if ifaceName else self.name + "-eth0"
-            cmd = 'get_if_addr("' + str( ifaceName ) + '")'
-            self.handle.sendline( cmd )
-            self.handle.expect( self.scapyPrompt )
-
-            pattern = r'(((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))'
-            match = re.search( pattern, self.handle.before )
-            if match:
-                # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
-                return match.group()
-            else:
-                return None
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": Command timed out" )
-            return None
-        except pexpect.EOF:
-            main.log.exception( self.name + ": connection closed." )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
 
 if __name__ != "__main__":
     sys.modules[ __name__ ] = MininetCliDriver()
diff --git a/TestON/drivers/common/cli/emulator/scapyclidriver.py b/TestON/drivers/common/cli/emulator/scapyclidriver.py
index a083183..39c5c4d 100644
--- a/TestON/drivers/common/cli/emulator/scapyclidriver.py
+++ b/TestON/drivers/common/cli/emulator/scapyclidriver.py
@@ -700,6 +700,7 @@
             sendCmd += ', timeout=' + str( timeout ) + ')'
             self.handle.sendline( sendCmd )
             self.handle.expect( self.scapyPrompt )
+            # main.log.warn( "Send packet response: {}".format( self.handle.before ) )
             if "Traceback" in self.handle.before:
                 # KeyError, SyntaxError, ...
                 main.log.error( "Error in sending command: " + self.handle.before )
@@ -739,7 +740,7 @@
             self.handle.expect( self.scapyPrompt )
             cmd = 'pkt = sniff(count = ' + str( sniffCount ) +\
                   ', filter = "' + str( pktFilter ) + '")'
-            print self.name + ' > ' + cmd
+            main.log.info( "Filter on " + self.name + ' > ' + cmd )
             self.handle.sendline( cmd )
             self.handle.expect( '"\)\r\n' )
             # TODO: parse this?
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index e1d298a..a3e2b6a 100644
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -151,7 +151,10 @@
                         # ONOS didn't fully load, and logout command isn't working
                         # or the command timed out
                         self.handle.send( "\x04" )  # send ctrl-d
-                        self.handle.expect( "\$" )
+                        try:
+                            self.handle.expect( "\$" )
+                        except pexpect.TIMEOUT:
+                            main.log.error( "ONOS did not respond to 'logout' or CTRL-d" )
                         return main.TRUE
                     else: # some other output
                         main.log.warn( "Unknown repsonse to logout command: '{}'",
@@ -301,11 +304,93 @@
             main.cleanup()
             main.exit()
 
-    def log( self, cmdStr, level="" ):
+    def startCellCli( self, karafTimeout="",
+                      commandlineTimeout=10, onosStartTimeout=60 ):
+        """
+        Start CLI on onos ecll handle.
+
+        karafTimeout is an optional argument. karafTimeout value passed
+        by user would be used to set the current karaf shell idle timeout.
+        Note that when ever this property is modified the shell will exit and
+        the subsequent login would reflect new idle timeout.
+        Below is an example to start a session with 60 seconds idle timeout
+        ( input value is in milliseconds ):
+
+        tValue = "60000"
+
+        Note: karafTimeout is left as str so that this could be read
+        and passed to startOnosCli from PARAMS file as str.
+        """
+
+        try:
+            self.handle.sendline( "" )
+            x = self.handle.expect( [
+                "\$", "onos>" ], commandlineTimeout)
+
+            if x == 1:
+                main.log.info( "ONOS cli is already running" )
+                return main.TRUE
+
+            # Wait for onos start ( -w ) and enter onos cli
+            self.handle.sendline( "/opt/onos/bin/onos" )
+            i = self.handle.expect( [
+                "onos>",
+                pexpect.TIMEOUT ], onosStartTimeout )
+
+            if i == 0:
+                main.log.info( self.name + " CLI Started successfully" )
+                if karafTimeout:
+                    self.handle.sendline(
+                        "config:property-set -p org.apache.karaf.shell\
+                                 sshIdleTimeout " +
+                        karafTimeout )
+                    self.handle.expect( "\$" )
+                    self.handle.sendline( "/opt/onos/bin/onos" )
+                    self.handle.expect( "onos>" )
+                return main.TRUE
+            else:
+                # If failed, send ctrl+c to process and try again
+                main.log.info( "Starting CLI failed. Retrying..." )
+                self.handle.send( "\x03" )
+                self.handle.sendline( "/opt/onos/bin/onos" )
+                i = self.handle.expect( [ "onos>", pexpect.TIMEOUT ],
+                                        timeout=30 )
+                if i == 0:
+                    main.log.info( self.name + " CLI Started " +
+                                   "successfully after retry attempt" )
+                    if karafTimeout:
+                        self.handle.sendline(
+                            "config:property-set -p org.apache.karaf.shell\
+                                    sshIdleTimeout " +
+                            karafTimeout )
+                        self.handle.expect( "\$" )
+                        self.handle.sendline( "/opt/onos/bin/onos" )
+                        self.handle.expect( "onos>" )
+                    return main.TRUE
+                else:
+                    main.log.error( "Connection to CLI " +
+                                    self.name + " timeout" )
+                    return main.FALSE
+
+        except TypeError:
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def log( self, cmdStr, level="",noExit=False):
         """
             log  the commands in the onos CLI.
             returns main.TRUE on success
             returns main.FALSE if Error occurred
+            if noExit is True, TestON will not exit, but clean up
             Available level: DEBUG, TRACE, INFO, WARN, ERROR
             Level defaults to INFO
         """
@@ -314,6 +399,54 @@
             if level:
                 lvlStr = "--level=" + level
 
+            self.handle.sendline( "log:log " + lvlStr + " " + cmdStr )
+            self.handle.expect( "log:log" )
+            self.handle.expect( "onos>" )
+
+            response = self.handle.before
+            if re.search( "Error", response ):
+                return main.FALSE
+            return main.TRUE
+        except pexpect.TIMEOUT:
+            main.log.exception( self.name + ": TIMEOUT exception found" )
+            if noExit:
+                main.cleanup()
+                return None
+            else:
+                main.cleanup()
+                main.exit()
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            if noExit:
+                main.cleanup()
+                return None
+            else:
+                main.cleanup()
+                main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            if noExit:
+                main.cleanup()
+                return None
+            else:
+                main.cleanup()
+                main.exit()
+
+    def sendline( self, cmdStr, showResponse=False, debug=False, timeout=10, noExit=False ):
+        """
+        Send a completely user specified string to
+        the onos> prompt. Use this function if you have
+        a very specific command to send.
+
+        if noExit is True, TestON will not exit, but clean up
+
+        Warning: There are no sanity checking to commands
+        sent using this method.
+
+        """
+        try:
+            # Try to reconnect if disconnected from cli
             self.handle.sendline( "" )
             i = self.handle.expect( [ "onos>", "\$", pexpect.TIMEOUT ] )
             if i == 1:
@@ -333,41 +466,11 @@
             if i == 2:
                 self.handle.sendline( "" )
                 self.handle.expect( "onos>" )
-            self.handle.sendline( "log:log " + lvlStr + " " + cmdStr )
-            self.handle.expect( "log:log" )
-            self.handle.expect( "onos>" )
 
-            response = self.handle.before
-            if re.search( "Error", response ):
-                return main.FALSE
-            return main.TRUE
-        except pexpect.TIMEOUT:
-            main.log.exception( self.name + ": TIMEOUT exception found" )
-            main.cleanup()
-            main.exit()
-        except pexpect.EOF:
-            main.log.error( self.name + ": EOF exception found" )
-            main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def sendline( self, cmdStr, showResponse=False, debug=False, timeout=10 ):
-        """
-        Send a completely user specified string to
-        the onos> prompt. Use this function if you have
-        a very specific command to send.
-
-        Warning: There are no sanity checking to commands
-        sent using this method.
-
-        """
-        try:
-            logStr = "\"Sending CLI command: '" + cmdStr + "'\""
-            self.log( logStr )
+            if debug:
+                # NOTE: This adds and average of .4 seconds per call
+                logStr = "\"Sending CLI command: '" + cmdStr + "'\""
+                self.log( logStr,noExit=noExit )
             self.handle.sendline( cmdStr )
             i = self.handle.expect( ["onos>", "\$"], timeout )
             response = self.handle.before
@@ -415,6 +518,7 @@
             return None
         except IndexError:
             main.log.exception( self.name + ": Object not as expected" )
+            main.log.debug( "response: {}".format( repr( response ) ) )
             return None
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
@@ -422,12 +526,20 @@
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanup()
-            main.exit()
+            if noExit:
+                main.cleanup()
+                return None
+            else:
+                main.cleanup()
+                main.exit()
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
+            if noExit:
+                main.cleanup()
+                return None
+            else:
+                main.cleanup()
+                main.exit()
 
     # IMPORTANT NOTE:
     # For all cli commands, naming convention should match
@@ -449,6 +561,7 @@
             cmdStr = "add-node " + str( nodeId ) + " " +\
                 str( ONOSIp ) + " " + str( tcpPort )
             handle = self.sendline( cmdStr )
+            assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             if re.search( "Error", handle ):
                 main.log.error( "Error in adding node" )
@@ -484,6 +597,7 @@
 
             cmdStr = "remove-node " + str( nodeId )
             handle = self.sendline( cmdStr )
+            assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             if re.search( "Error", handle ):
                 main.log.error( "Error in removing node" )
@@ -519,6 +633,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -670,6 +785,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -733,7 +849,7 @@
             main.cleanup()
             main.exit()
 
-    def links( self, jsonFormat=True ):
+    def links( self, jsonFormat=True, timeout=30 ):
         """
         Lists all core links
         Optional argument:
@@ -743,7 +859,7 @@
             cmdStr = "links"
             if jsonFormat:
                 cmdStr += " -j"
-            handle = self.sendline( cmdStr )
+            handle = self.sendline( cmdStr, timeout=timeout )
             assert "Command not found:" not in handle, handle
             return handle
         except AssertionError:
@@ -1032,11 +1148,14 @@
             main.cleanup()
             main.exit()
 
-    def addHostIntent( self, hostIdOne, hostIdTwo ):
+    def addHostIntent( self, hostIdOne, hostIdTwo, vlanId="", setVlan="" ):
         """
         Required:
             * hostIdOne: ONOS host id for host1
             * hostIdTwo: ONOS host id for host2
+        Optional:
+            * vlanId: specify a VLAN id for the intent
+            * setVlan: specify a VLAN id treatment
         Description:
             Adds a host-to-host intent ( bidirectional ) by
             specifying the two hosts.
@@ -1044,8 +1163,12 @@
             A string of the intent id or None on Error
         """
         try:
-            cmdStr = "add-host-intent " + str( hostIdOne ) +\
-                " " + str( hostIdTwo )
+            cmdStr = "add-host-intent "
+            if vlanId:
+                cmdStr += "-v " + str( vlanId ) + " "
+            if setVlan:
+                cmdStr += "--setVlan " + str( vlanId ) + " "
+            cmdStr += str( hostIdOne ) + " " + str( hostIdTwo )
             handle = self.sendline( cmdStr )
             assert "Command not found:" not in handle, handle
             if re.search( "Error", handle ):
@@ -1141,7 +1264,9 @@
             ipSrc="",
             ipDst="",
             tcpSrc="",
-            tcpDst="" ):
+            tcpDst="",
+            vlanId="",
+            setVlan="" ):
         """
         Required:
             * ingressDevice: device id of ingress device
@@ -1158,6 +1283,8 @@
             * ipDst: specify ip destination address
             * tcpSrc: specify tcp source port
             * tcpDst: specify tcp destination port
+            * vlanId: specify vlan ID
+            * setVlan: specify a VLAN id treatment
         Description:
             Adds a point-to-point intent ( uni-directional ) by
             specifying device id's and optional fields
@@ -1169,36 +1296,32 @@
               intent via cli
         """
         try:
-            # If there are no optional arguments
-            if not ethType and not ethSrc and not ethDst\
-                    and not bandwidth and not lambdaAlloc \
-                    and not ipProto and not ipSrc and not ipDst \
-                    and not tcpSrc and not tcpDst:
-                cmd = "add-point-intent"
+            cmd = "add-point-intent"
 
-            else:
-                cmd = "add-point-intent"
-
-                if ethType:
-                    cmd += " --ethType " + str( ethType )
-                if ethSrc:
-                    cmd += " --ethSrc " + str( ethSrc )
-                if ethDst:
-                    cmd += " --ethDst " + str( ethDst )
-                if bandwidth:
-                    cmd += " --bandwidth " + str( bandwidth )
-                if lambdaAlloc:
-                    cmd += " --lambda "
-                if ipProto:
-                    cmd += " --ipProto " + str( ipProto )
-                if ipSrc:
-                    cmd += " --ipSrc " + str( ipSrc )
-                if ipDst:
-                    cmd += " --ipDst " + str( ipDst )
-                if tcpSrc:
-                    cmd += " --tcpSrc " + str( tcpSrc )
-                if tcpDst:
-                    cmd += " --tcpDst " + str( tcpDst )
+            if ethType:
+                cmd += " --ethType " + str( ethType )
+            if ethSrc:
+                cmd += " --ethSrc " + str( ethSrc )
+            if ethDst:
+                cmd += " --ethDst " + str( ethDst )
+            if bandwidth:
+                cmd += " --bandwidth " + str( bandwidth )
+            if lambdaAlloc:
+                cmd += " --lambda "
+            if ipProto:
+                cmd += " --ipProto " + str( ipProto )
+            if ipSrc:
+                cmd += " --ipSrc " + str( ipSrc )
+            if ipDst:
+                cmd += " --ipDst " + str( ipDst )
+            if tcpSrc:
+                cmd += " --tcpSrc " + str( tcpSrc )
+            if tcpDst:
+                cmd += " --tcpDst " + str( tcpDst )
+            if vlanId:
+                cmd += " -v " + str( vlanId )
+            if setVlan:
+                cmd += " --setVlan " + str( setVlan )
 
             # Check whether the user appended the port
             # or provided it as an input
@@ -1277,7 +1400,10 @@
             tcpSrc="",
             tcpDst="",
             setEthSrc="",
-            setEthDst="" ):
+            setEthDst="",
+            vlanId="",
+            setVlan="",
+            partial=False ):
         """
         Note:
             This function assumes the format of all ingress devices
@@ -1302,6 +1428,8 @@
             * tcpDst: specify tcp destination port
             * setEthSrc: action to Rewrite Source MAC Address
             * setEthDst: action to Rewrite Destination MAC Address
+            * vlanId: specify vlan Id
+            * setVlan: specify VLAN Id treatment
         Description:
             Adds a multipoint-to-singlepoint intent ( uni-directional ) by
             specifying device id's and optional fields
@@ -1313,41 +1441,38 @@
               intent via cli
         """
         try:
-            # If there are no optional arguments
-            if not ethType and not ethSrc and not ethDst\
-                    and not bandwidth and not lambdaAlloc\
-                    and not ipProto and not ipSrc and not ipDst\
-                    and not tcpSrc and not tcpDst and not setEthSrc\
-                    and not setEthDst:
-                cmd = "add-multi-to-single-intent"
+            cmd = "add-multi-to-single-intent"
 
-            else:
-                cmd = "add-multi-to-single-intent"
-
-                if ethType:
-                    cmd += " --ethType " + str( ethType )
-                if ethSrc:
-                    cmd += " --ethSrc " + str( ethSrc )
-                if ethDst:
-                    cmd += " --ethDst " + str( ethDst )
-                if bandwidth:
-                    cmd += " --bandwidth " + str( bandwidth )
-                if lambdaAlloc:
-                    cmd += " --lambda "
-                if ipProto:
-                    cmd += " --ipProto " + str( ipProto )
-                if ipSrc:
-                    cmd += " --ipSrc " + str( ipSrc )
-                if ipDst:
-                    cmd += " --ipDst " + str( ipDst )
-                if tcpSrc:
-                    cmd += " --tcpSrc " + str( tcpSrc )
-                if tcpDst:
-                    cmd += " --tcpDst " + str( tcpDst )
-                if setEthSrc:
-                    cmd += " --setEthSrc " + str( setEthSrc )
-                if setEthDst:
-                    cmd += " --setEthDst " + str( setEthDst )
+            if ethType:
+                cmd += " --ethType " + str( ethType )
+            if ethSrc:
+                cmd += " --ethSrc " + str( ethSrc )
+            if ethDst:
+                cmd += " --ethDst " + str( ethDst )
+            if bandwidth:
+                cmd += " --bandwidth " + str( bandwidth )
+            if lambdaAlloc:
+                cmd += " --lambda "
+            if ipProto:
+                cmd += " --ipProto " + str( ipProto )
+            if ipSrc:
+                cmd += " --ipSrc " + str( ipSrc )
+            if ipDst:
+                cmd += " --ipDst " + str( ipDst )
+            if tcpSrc:
+                cmd += " --tcpSrc " + str( tcpSrc )
+            if tcpDst:
+                cmd += " --tcpDst " + str( tcpDst )
+            if setEthSrc:
+                cmd += " --setEthSrc " + str( setEthSrc )
+            if setEthDst:
+                cmd += " --setEthDst " + str( setEthDst )
+            if vlanId:
+                cmd += " -v " + str( vlanId )
+            if setVlan:
+                cmd += " --setVlan " + str( setVlan )
+            if partial:
+                cmd += " --partial"
 
             # Check whether the user appended the port
             # or provided it as an input
@@ -1430,7 +1555,10 @@
             tcpSrc="",
             tcpDst="",
             setEthSrc="",
-            setEthDst="" ):
+            setEthDst="",
+            vlanId="",
+            setVlan="",
+            partial=False ):
         """
         Note:
             This function assumes the format of all egress devices
@@ -1455,6 +1583,8 @@
             * tcpDst: specify tcp destination port
             * setEthSrc: action to Rewrite Source MAC Address
             * setEthDst: action to Rewrite Destination MAC Address
+            * vlanId: specify vlan Id
+            * setVlan: specify VLAN ID treatment
         Description:
             Adds a singlepoint-to-multipoint intent ( uni-directional ) by
             specifying device id's and optional fields
@@ -1466,41 +1596,38 @@
               intent via cli
         """
         try:
-            # If there are no optional arguments
-            if not ethType and not ethSrc and not ethDst\
-                    and not bandwidth and not lambdaAlloc\
-                    and not ipProto and not ipSrc and not ipDst\
-                    and not tcpSrc and not tcpDst and not setEthSrc\
-                    and not setEthDst:
-                cmd = "add-single-to-multi-intent"
+            cmd = "add-single-to-multi-intent"
 
-            else:
-                cmd = "add-single-to-multi-intent"
-
-                if ethType:
-                    cmd += " --ethType " + str( ethType )
-                if ethSrc:
-                    cmd += " --ethSrc " + str( ethSrc )
-                if ethDst:
-                    cmd += " --ethDst " + str( ethDst )
-                if bandwidth:
-                    cmd += " --bandwidth " + str( bandwidth )
-                if lambdaAlloc:
-                    cmd += " --lambda "
-                if ipProto:
-                    cmd += " --ipProto " + str( ipProto )
-                if ipSrc:
-                    cmd += " --ipSrc " + str( ipSrc )
-                if ipDst:
-                    cmd += " --ipDst " + str( ipDst )
-                if tcpSrc:
-                    cmd += " --tcpSrc " + str( tcpSrc )
-                if tcpDst:
-                    cmd += " --tcpDst " + str( tcpDst )
-                if setEthSrc:
-                    cmd += " --setEthSrc " + str( setEthSrc )
-                if setEthDst:
-                    cmd += " --setEthDst " + str( setEthDst )
+            if ethType:
+                cmd += " --ethType " + str( ethType )
+            if ethSrc:
+                cmd += " --ethSrc " + str( ethSrc )
+            if ethDst:
+                cmd += " --ethDst " + str( ethDst )
+            if bandwidth:
+                cmd += " --bandwidth " + str( bandwidth )
+            if lambdaAlloc:
+                cmd += " --lambda "
+            if ipProto:
+                cmd += " --ipProto " + str( ipProto )
+            if ipSrc:
+                cmd += " --ipSrc " + str( ipSrc )
+            if ipDst:
+                cmd += " --ipDst " + str( ipDst )
+            if tcpSrc:
+                cmd += " --tcpSrc " + str( tcpSrc )
+            if tcpDst:
+                cmd += " --tcpDst " + str( tcpDst )
+            if setEthSrc:
+                cmd += " --setEthSrc " + str( setEthSrc )
+            if setEthDst:
+                cmd += " --setEthDst " + str( setEthDst )
+            if vlanId:
+                cmd += " -v " + str( vlanId )
+            if setVlan:
+                cmd += " --setVlan " + str( setVlan )
+            if partial:
+                cmd += " --partial"
 
             # Check whether the user appended the port
             # or provided it as an input
@@ -1615,43 +1742,34 @@
               intent via cli
         """
         try:
-            # If there are no optional arguments
-            if not ethType and not ethSrc and not ethDst\
-                    and not bandwidth and not lambdaAlloc \
-                    and not ipProto and not ipSrc and not ipDst \
-                    and not tcpSrc and not tcpDst and not ingressLabel \
-                    and not egressLabel:
-                cmd = "add-mpls-intent"
+            cmd = "add-mpls-intent"
 
-            else:
-                cmd = "add-mpls-intent"
-
-                if ethType:
-                    cmd += " --ethType " + str( ethType )
-                if ethSrc:
-                    cmd += " --ethSrc " + str( ethSrc )
-                if ethDst:
-                    cmd += " --ethDst " + str( ethDst )
-                if bandwidth:
-                    cmd += " --bandwidth " + str( bandwidth )
-                if lambdaAlloc:
-                    cmd += " --lambda "
-                if ipProto:
-                    cmd += " --ipProto " + str( ipProto )
-                if ipSrc:
-                    cmd += " --ipSrc " + str( ipSrc )
-                if ipDst:
-                    cmd += " --ipDst " + str( ipDst )
-                if tcpSrc:
-                    cmd += " --tcpSrc " + str( tcpSrc )
-                if tcpDst:
-                    cmd += " --tcpDst " + str( tcpDst )
-                if ingressLabel:
-                    cmd += " --ingressLabel " + str( ingressLabel )
-                if egressLabel:
-                    cmd += " --egressLabel " + str( egressLabel )
-                if priority:
-                    cmd += " --priority " + str( priority )
+            if ethType:
+                cmd += " --ethType " + str( ethType )
+            if ethSrc:
+                cmd += " --ethSrc " + str( ethSrc )
+            if ethDst:
+                cmd += " --ethDst " + str( ethDst )
+            if bandwidth:
+                cmd += " --bandwidth " + str( bandwidth )
+            if lambdaAlloc:
+                cmd += " --lambda "
+            if ipProto:
+                cmd += " --ipProto " + str( ipProto )
+            if ipSrc:
+                cmd += " --ipSrc " + str( ipSrc )
+            if ipDst:
+                cmd += " --ipDst " + str( ipDst )
+            if tcpSrc:
+                cmd += " --tcpSrc " + str( tcpSrc )
+            if tcpDst:
+                cmd += " --tcpDst " + str( tcpDst )
+            if ingressLabel:
+                cmd += " --ingressLabel " + str( ingressLabel )
+            if egressLabel:
+                cmd += " --egressLabel " + str( egressLabel )
+            if priority:
+                cmd += " --priority " + str( priority )
 
             # Check whether the user appended the port
             # or provided it as an input
@@ -1754,7 +1872,7 @@
             main.cleanup()
             main.exit()
 
-    def removeAllIntents( self, purge=False, sync=False, app='org.onosproject.cli' ):
+    def removeAllIntents( self, purge=False, sync=False, app='org.onosproject.cli', timeout=30 ):
         """
         Description:
             Remove all the intents
@@ -1773,7 +1891,7 @@
                 cmdStr += " -s"
 
             cmdStr += " " + app
-            handle = self.sendline( cmdStr )
+            handle = self.sendline( cmdStr, timeout=timeout )
             assert "Command not found:" not in handle, handle
             if re.search( "Error", handle ):
                 main.log.error( "Error in removing intent" )
@@ -1937,19 +2055,18 @@
 
     def getIntentState(self, intentsId, intentsJson=None):
         """
-            Check 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.
-            Returns a dictionary with intent IDs as the key and its
-            corresponding states as the values
-            Parameters:
-            intentId: intent ID (string type)
+        Description:
+            Gets intent state. Accepts a single intent ID (string type) or a
+            list of intent IDs.
+        Parameters:
+            intentsId: intent ID, both string type and list type are acceptable
             intentsJson: parsed json object from the onos:intents api
-            Returns:
-            state = An intent's state- INSTALL,WITHDRAWN etc.
-            stateDict = Dictionary of intent's state. intent ID as the keys and
-            state as the values.
+        Returns:
+            Returns the state (string type) of the ID if a single intent ID is
+            accepted.
+            Returns a list of dictionaries if a list of intent IDs is accepted,
+            and each dictionary maps 'id' to the Intent ID and 'state' to
+            corresponding intent state.
         """
         try:
             state = "State is Undefined"
@@ -2061,6 +2178,55 @@
             main.cleanup()
             main.exit()
 
+    def compareIntent( self, intentDict ):
+        """
+        Description:
+            Compare the intent ids and states provided in the argument with all intents in ONOS
+        Return:
+            Returns main.TRUE if the two sets of intents match exactly, otherwise main.FALSE
+        Arguments:
+            intentDict: a dictionary which maps intent ids to intent states
+        """
+        try:
+            intentsRaw = self.intents()
+            intentsJson = json.loads( intentsRaw )
+            intentDictONOS = {}
+            for intent in intentsJson:
+                intentDictONOS[ intent[ 'id' ] ] = intent[ 'state' ]
+            if len( intentDict ) != len( intentDictONOS ):
+                main.log.info( self.name + ": expected intent count does not match that in ONOS, " +
+                               str( len( intentDict ) ) + " expected and " +
+                               str( len( intentDictONOS ) ) + " actual" )
+                return main.FALSE
+            returnValue = main.TRUE
+            for intentID in intentDict.keys():
+                if not intentID in intentDictONOS.keys():
+                    main.log.debug( self.name + ": intent ID - " + intentID + " is not in ONOS" )
+                    returnValue = main.FALSE
+                elif intentDict[ intentID ] != intentDictONOS[ intentID ]:
+                    main.log.debug( self.name + ": intent ID - " + intentID +
+                                    " expected state is " + intentDict[ intentID ] +
+                                    " but actual state is " + intentDictONOS[ intentID ] )
+                    returnValue = main.FALSE
+            if returnValue == main.TRUE:
+                main.log.info( self.name + ": all intent IDs and states match that in ONOS" )
+            return returnValue
+        except KeyError:
+            main.log.exception( self.name + ": KeyError exception found" )
+            return main.ERROR
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, intentsRaw ) )
+            return main.ERROR
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
     def checkIntentSummary( self, timeout=60 ):
         """
         Description:
@@ -2101,8 +2267,11 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": ONOS timeout" )
+            return None
 
-    def flows( self, state="", jsonFormat=True, timeout=60 ):
+    def flows( self, state="", jsonFormat=True, timeout=60, noExit=False ):
         """
         Optional:
             * jsonFormat: enable output formatting in json
@@ -2114,7 +2283,7 @@
             if jsonFormat:
                 cmdStr += " -j "
             cmdStr += state
-            handle = self.sendline( cmdStr, timeout=timeout )
+            handle = self.sendline( cmdStr, timeout=timeout, noExit=noExit )
             assert "Command not found:" not in handle, handle
             if re.search( "Error:", handle ):
                 main.log.error( self.name + ": flows() response: " +
@@ -2139,8 +2308,11 @@
             main.cleanup()
             main.exit()
 
+    def checkFlowCount(self, min=0, timeout=60 ):
+        count = int(self.getTotalFlowsNum( timeout=timeout ))
+        return count if (count > min) else False
 
-    def checkFlowsState( self, isPENDING=True, timeout=60 ):
+    def checkFlowsState( self, isPENDING=True, timeout=60,noExit=False ):
         """
         Description:
             Check the if all the current flows are in ADDED state
@@ -2160,7 +2332,12 @@
             statesCount = [0, 0, 0, 0]
             for s in states:
                 rawFlows = self.flows( state=s, timeout = timeout )
-                checkedStates.append( json.loads( rawFlows ) )
+                if rawFlows:
+                    # if we didn't get flows or flows function return None, we should return
+                    # main.Flase
+                    checkedStates.append( json.loads( rawFlows ) )
+                else:
+                    return main.FALSE
             for i in range( len( states ) ):
                 for c in checkedStates[i]:
                     try:
@@ -2180,6 +2357,10 @@
         except ( TypeError, ValueError ):
             main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawFlows ) )
             return None
+
+        except AssertionError:
+            main.log.exception( "" )
+            return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":    " + self.handle.before )
@@ -2189,9 +2370,13 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": ONOS timeout" )
+            return None
+
 
     def pushTestIntents( self, ingress, egress, batchSize, offset="",
-                         options="", timeout=10, background = False ):
+                         options="", timeout=10, background = False, noExit=False ):
         """
         Description:
             Push a number of intents in a batch format to
@@ -2220,7 +2405,7 @@
                                                                 batchSize,
                                                                 offset,
                                                                 back )
-            response = self.sendline( cmd, timeout=timeout )
+            response = self.sendline( cmd, timeout=timeout, noExit=noExit )
             assert "Command not found:" not in response, response
             main.log.info( response )
             if response == None:
@@ -2248,7 +2433,7 @@
             main.cleanup()
             main.exit()
 
-    def getTotalFlowsNum( self, timeout=60 ):
+    def getTotalFlowsNum( self, timeout=60, noExit=False ):
         """
         Description:
             Get the number of ADDED flows.
@@ -2259,7 +2444,7 @@
         try:
             # get total added flows number
             cmd = "flows -s|grep ADDED|wc -l"
-            totalFlows = self.sendline( cmd, timeout=timeout )
+            totalFlows = self.sendline( cmd, timeout=timeout, noExit=noExit )
 
             if totalFlows == None:
                 # if timeout, we will get total number of all flows, and subtract other states
@@ -2269,7 +2454,7 @@
                 statesCount = [0, 0, 0, 0]
 
                 # get total flows from summary
-                response = json.loads( self.sendline( "summary -j", timeout=timeout ) )
+                response = json.loads( self.sendline( "summary -j", timeout=timeout, noExit=noExit ) )
                 totalFlows = int( response.get("flows") )
 
                 for s in states:
@@ -2291,10 +2476,10 @@
 
                 return totalFlows
 
-            return totalFlows
+            return int(totalFlows)
 
-        except TypeError:
-            main.log.exception( self.name + ": Object not as expected" )
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawFlows ) )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2305,8 +2490,11 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": ONOS timeout" )
+            return None
 
-    def getTotalIntentsNum( self ):
+    def getTotalIntentsNum( self, timeout=60 ):
         """
         Description:
             Get the total number of intents, include every states.
@@ -2315,13 +2503,13 @@
         """
         try:
             cmd = "summary -j"
-            response = self.sendline( cmd )
+            response = self.sendline( cmd, timeout=timeout )
             if response == None:
                 return  -1
             response = json.loads( response )
             return int( response.get("intents") )
-        except TypeError:
-            main.log.exception( self.name + ": Object not as expected" )
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, response ) )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2569,28 +2757,56 @@
             main.cleanup()
             main.exit()
 
-    def checkStatus( self, ip, numoswitch, numolink, logLevel="info" ):
+    def getTopology( self, topologyOutput ):
+        """
+        Definition:
+            Loads a json topology output
+        Return:
+            topology = current ONOS topology
+        """
+        import json
+        try:
+            # either onos:topology or 'topology' will work in CLI
+            topology = json.loads(topologyOutput)
+            main.log.debug( topology )
+            return topology
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, topologyOutput ) )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def checkStatus(
+            self,
+            topologyResult,
+            numoswitch,
+            numolink,
+            logLevel="info" ):
         """
         Checks the number of switches & links that ONOS sees against the
         supplied values. By default this will report to main.log, but the
-        log level can be specified.
+        log level can be specific.
 
-        Params: ip = ip used for the onos cli
+        Params: topologyResult = the output of topology command
                 numoswitch = expected number of switches
                 numolink = expected number of links
-                logLevel = level to log to. Currently accepts
-                'info', 'warn' and 'report'
-
-
-        logLevel can
+                logLevel = level to log to.
+                Currently accepts 'info', 'warn' and 'report'
 
         Returns: main.TRUE if the number of switches and links are correct,
                  main.FALSE if the number of switches and links is incorrect,
                  and main.ERROR otherwise
         """
         try:
-            topology = self.getTopology( ip )
-            if topology == {}:
+            topology = self.getTopology( topologyResult )
+            if topology == {} or topology == None:
                 return main.ERROR
             output = ""
             # Is the number of switches is what we expected
@@ -2601,29 +2817,27 @@
             switchCheck = ( int( devices ) == int( numoswitch ) )
             # Is the number of links is what we expected
             linkCheck = ( int( links ) == int( numolink ) )
-            if ( switchCheck and linkCheck ):
+            if switchCheck and linkCheck:
                 # We expected the correct numbers
-                output += "The number of links and switches match " +\
-                          "what was expected"
+                output = output + "The number of links and switches match "\
+                    + "what was expected"
                 result = main.TRUE
             else:
-                output += "The number of links and switches does not match " +\
-                          "what was expected"
+                output = output + \
+                    "The number of links and switches does not match " + \
+                    "what was expected"
                 result = main.FALSE
-            output = output + "\n ONOS sees %i devices (%i expected) \
-                    and %i links (%i expected)" % (
-                int( devices ), int( numoswitch ), int( links ),
-                int( numolink ) )
+            output = output + "\n ONOS sees %i devices" % int( devices )
+            output = output + " (%i expected) " % int( numoswitch )
+            output = output + "and %i links " % int( links )
+            output = output + "(%i expected)" % int( numolink )
             if logLevel == "report":
                 main.log.report( output )
             elif logLevel == "warn":
                 main.log.warn( output )
             else:
-                main.log.info( self.name + ": " + output )
+                main.log.info( output )
             return result
-        except TypeError:
-            main.log.exception( self.name + ": Object not as expected" )
-            return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":    " + self.handle.before )
@@ -2864,12 +3078,12 @@
             dpid = str( dpid )
             cmdStr = "onos:ports -e " + dpid + " | wc -l"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             if re.search( "No such device", output ):
                 main.log.error( "Error in getting ports" )
                 return ( output, "Error" )
-            else:
-                return output
+            return output
         except AssertionError:
             main.log.exception( "" )
             return None
@@ -2894,12 +3108,12 @@
             dpid = str( dpid )
             cmdStr = "onos:links " + dpid + " | grep ACTIVE | wc -l"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             if re.search( "No such device", output ):
                 main.log.error( "Error in getting ports " )
                 return ( output, "Error " )
-            else:
-                return output
+            return output
         except AssertionError:
             main.log.exception( "" )
             return None
@@ -2923,12 +3137,12 @@
         try:
             cmdStr = "onos:intents | grep id="
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             if re.search( "Error", output ):
                 main.log.error( "Error in getting ports" )
                 return ( output, "Error" )
-            else:
-                return output
+            return output
         except AssertionError:
             main.log.exception( "" )
             return None
@@ -2981,6 +3195,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -3010,6 +3225,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -3038,6 +3254,7 @@
         try:
             cmdStr = "onos:leaders -j"
             rawOutput = self.sendline( cmdStr )
+            assert rawOutput is not None, "Error in sendline"
             assert "Command not found:" not in rawOutput, rawOutput
             output = json.loads( rawOutput )
             results = []
@@ -3073,6 +3290,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -3111,6 +3329,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             return output
         except AssertionError:
@@ -3144,6 +3363,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             return output
@@ -3450,6 +3670,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             return output
@@ -3587,6 +3808,7 @@
             elif short:
                 baseStr += " -s"
             output = self.sendline( baseStr + cmdStr + componentStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             return output
@@ -3628,6 +3850,7 @@
             if value is not None:
                 cmdStr += " " + str( value )
             output = self.sendline( baseStr + cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             if value and check:
@@ -3679,6 +3902,7 @@
         try:
             cmdStr = "set-test-add " + str( setName ) + " " + str( values )
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             try:
                 # TODO: Maybe make this less hardcoded
@@ -3694,6 +3918,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Error executing command" not in output
             positiveMatch = "\[(.*)\] was added to the set " + str( setName )
             negativeMatch = "\[(.*)\] was already in set " + str( setName )
@@ -3748,6 +3973,7 @@
                 cmdStr += str( setName ) + " " + str( values )
             output = self.sendline( cmdStr )
             try:
+                assert output is not None, "Error in sendline"
                 # TODO: Maybe make this less hardcoded
                 # ConsistentMap Exceptions
                 assert "org.onosproject.store.service" not in output
@@ -3761,6 +3987,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -3847,6 +4074,7 @@
             cmdStr += setName + " " + values
             output = self.sendline( cmdStr )
             try:
+                assert output is not None, "Error in sendline"
                 # TODO: Maybe make this less hardcoded
                 # ConsistentMap Exceptions
                 assert "org.onosproject.store.service" not in output
@@ -3860,6 +4088,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -3940,6 +4169,7 @@
             cmdStr += setName
             output = self.sendline( cmdStr )
             try:
+                assert output is not None, "Error in sendline"
                 # TODO: Maybe make this less hardcoded
                 # ConsistentMap Exceptions
                 assert "org.onosproject.store.service" not in output
@@ -3953,6 +4183,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -4006,6 +4237,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -4046,6 +4278,7 @@
                 cmdStr += " " + str( delta )
             output = self.sendline( cmdStr )
             try:
+                assert output is not None, "Error in sendline"
                 # TODO: Maybe make this less hardcoded
                 # ConsistentMap Exceptions
                 assert "org.onosproject.store.service" not in output
@@ -4059,6 +4292,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -4108,6 +4342,7 @@
                 cmdStr += " " + str( delta )
             output = self.sendline( cmdStr )
             try:
+                assert output is not None, "Error in sendline"
                 # TODO: Maybe make this less hardcoded
                 # ConsistentMap Exceptions
                 assert "org.onosproject.store.service" not in output
@@ -4121,6 +4356,7 @@
                                "seconds before retrying." )
                 time.sleep( retryTime )  # Due to change in mastership
                 output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             assert "Error executing command" not in output, output
             main.log.info( self.name + ": " + output )
@@ -4150,7 +4386,7 @@
             main.cleanup()
             main.exit()
 
-    def summary( self, jsonFormat=True ):
+    def summary( self, jsonFormat=True, timeout=30 ):
         """
         Description: Execute summary command in onos
         Returns: json object ( summary -j ), returns main.FALSE if there is
@@ -4161,7 +4397,8 @@
             cmdStr = "summary"
             if jsonFormat:
                 cmdStr += " -j"
-            handle = self.sendline( cmdStr )
+            handle = self.sendline( cmdStr, timeout=timeout )
+            assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             assert "Error:" not in handle, handle
             if not handle:
@@ -4201,6 +4438,7 @@
             cmdStr = "transactional-map-test-get "
             cmdStr += keyName
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             try:
                 # TODO: Maybe make this less hardcoded
@@ -4214,6 +4452,7 @@
                 return None
             pattern = "Key-value pair \(" + keyName + ", (?P<value>.+)\) found."
             if "Key " + keyName + " not found." in output:
+                main.log.warn( output )
                 return None
             else:
                 match = re.search( pattern, output )
@@ -4266,6 +4505,7 @@
             cmdStr = "transactional-map-test-put "
             cmdStr += numKeys + " " + value
             output = self.sendline( cmdStr )
+            assert output is not None, "Error in sendline"
             assert "Command not found:" not in output, output
             try:
                 # TODO: Maybe make this less hardcoded
@@ -4323,6 +4563,7 @@
             if jsonFormat:
                 cmdStr += " -j"
             handle = self.sendline( cmdStr )
+            assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             return handle
         except AssertionError:
@@ -4350,6 +4591,7 @@
             if jsonFormat:
                 cmd += "-j "
             response = self.sendline( cmd + uri )
+            assert response is not None, "Error in sendline"
             assert "Command not found:" not in response, response
             return response
         except AssertionError:
@@ -4412,6 +4654,7 @@
                 else:
                     cmd += " {}:{}:{}".format( proto, item, port )
             response = self.sendline( cmd )
+            assert response is not None, "Error in sendline"
             assert "Command not found:" not in response, response
             if "Error" in response:
                 main.log.error( response )
@@ -4452,6 +4695,7 @@
             for d in device:
                 time.sleep( 1 )
                 response = self.sendline( "device-remove {}".format( d ) )
+                assert response is not None, "Error in sendline"
                 assert "Command not found:" not in response, response
                 if "Error" in response:
                     main.log.warn( "Error for device: {}\nResponse: {}".format( d, response ) )
@@ -4492,6 +4736,7 @@
             for h in host:
                 time.sleep( 1 )
                 response = self.sendline( "host-remove {}".format( h ) )
+                assert response is not None, "Error in sendline"
                 assert "Command not found:" not in response, response
                 if "Error" in response:
                     main.log.warn( "Error for host: {}\nResponse: {}".format( h, response ) )
@@ -4513,7 +4758,7 @@
             main.cleanup()
             main.exit()
 
-    def link( self, begin, end, state ):
+    def link( self, begin, end, state, timeout=30, showResponse=True ):
         '''
         Description:
             Bring link down or up in the null-provider.
@@ -4526,7 +4771,8 @@
         '''
         try:
             cmd =  "null-link null:{} null:{} {}".format( begin, end, state )
-            response = self.sendline( cmd, showResponse=True )
+            response = self.sendline( cmd, showResponse=showResponse, timeout=timeout )
+            assert response is not None, "Error in sendline"
             assert "Command not found:" not in response, response
             if "Error" in response or "Failure" in response:
                 main.log.error( response )
diff --git a/TestON/drivers/common/cli/onosdriver.py b/TestON/drivers/common/cli/onosdriver.py
old mode 100644
new mode 100755
index e0a1a6a..dde65d3
--- a/TestON/drivers/common/cli/onosdriver.py
+++ b/TestON/drivers/common/cli/onosdriver.py
@@ -25,7 +25,6 @@
 from requests.models import Response
 from drivers.common.clidriver import CLI
 
-
 class OnosDriver( CLI ):
 
     def __init__( self ):
@@ -194,28 +193,33 @@
             ret = main.TRUE
             self.handle.sendline( "onos-package" )
             self.handle.expect( "onos-package" )
-            i = self.handle.expect( [ "Downloading",
-                                      "tar.gz",
-                                      "\$",
-                                      "Unknown options" ],
-                                    opTimeout )
-            handle = str( self.handle.before + self.handle.after )
-            if i == 0:
-                # Give more time to download the file
-                self.handle.expect( "\$", opTimeout * 2 )
-                handle += str( self.handle.before )
-            elif i == 1:
-                self.handle.expect( "\$" )
-                handle += str( self.handle.before )
-            elif i == 2:
-                # This seems to be harmless, but may be a problem
-                main.log.warn( "onos-package output not as expected" )
-            elif i == 3:
-                # Incorrect usage
-                main.log.error( "onos-package does not recognize the given options" )
-                self.handle.expect( "\$" )
-                handle += str( self.handle.before )
-                ret = main.FALSE
+            while True:
+                i = self.handle.expect( [ "Downloading",
+                                          "Unknown options",
+                                          "No such file or directory",
+                                          "tar.gz",
+                                          "\$" ],
+                                        opTimeout )
+                handle = str( self.handle.before + self.handle.after )
+                if i == 0:
+                    # Give more time to download the file
+                    continue  # expect again
+                elif i == 1:
+                    # Incorrect usage
+                    main.log.error( "onos-package does not recognize the given options" )
+                    ret = main.FALSE
+                    continue  # expect again
+                elif i == 2:
+                    # File(s) not found
+                    main.log.error( "onos-package could not find a file or directory" )
+                    ret = main.FALSE
+                    continue  # expect again
+                elif i == 3:
+                    # tar.gz
+                    continue  # expect again
+                elif i == 4:
+                    # Prompt returned
+                    break
             main.log.info( "onos-package command returned: " + handle )
             # As long as the sendline does not time out,
             # return true. However, be careful to interpret
@@ -790,7 +794,7 @@
                 handleMore = self.handle.before
 
                 cell_result = handleBefore + handleAfter + handleMore
-                print cell_result
+                #print cell_result
                 if( re.search( "No such cell", cell_result ) ):
                     main.log.error( "Cell call returned: " + handleBefore +
                                handleAfter + handleMore )
@@ -1345,97 +1349,6 @@
             main.cleanup()
             main.exit()
 
-    def getTopology( self, topologyOutput ):
-        """
-        Definition:
-            Loads a json topology output
-        Return:
-            topology = current ONOS topology
-        """
-        import json
-        try:
-            # either onos:topology or 'topology' will work in CLI
-            topology = json.loads(topologyOutput)
-            print topology
-            return topology
-        except pexpect.EOF:
-            main.log.error( self.name + ": EOF exception found" )
-            main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
-    def checkStatus(
-            self,
-            topologyResult,
-            numoswitch,
-            numolink,
-            logLevel="info" ):
-        """
-        Checks the number of switches & links that ONOS sees against the
-        supplied values. By default this will report to main.log, but the
-        log level can be specific.
-
-        Params: ip = ip used for the onos cli
-                numoswitch = expected number of switches
-                numolink = expected number of links
-                logLevel = level to log to.
-                Currently accepts 'info', 'warn' and 'report'
-
-
-        logLevel can
-
-        Returns: main.TRUE if the number of switches and links are correct,
-                 main.FALSE if the number of switches and links is incorrect,
-                 and main.ERROR otherwise
-        """
-        try:
-            topology = self.getTopology( topologyResult )
-            if topology == {}:
-                return main.ERROR
-            output = ""
-            # Is the number of switches is what we expected
-            devices = topology.get( 'devices', False )
-            links = topology.get( 'links', False )
-            if not devices or not links:
-                return main.ERROR
-            switchCheck = ( int( devices ) == int( numoswitch ) )
-            # Is the number of links is what we expected
-            linkCheck = ( int( links ) == int( numolink ) )
-            if switchCheck and linkCheck:
-                # We expected the correct numbers
-                output = output + "The number of links and switches match "\
-                    + "what was expected"
-                result = main.TRUE
-            else:
-                output = output + \
-                    "The number of links and switches does not match " + \
-                    "what was expected"
-                result = main.FALSE
-            output = output + "\n ONOS sees %i devices" % int( devices )
-            output = output + " (%i expected) " % int( numoswitch )
-            output = output + "and %i links " % int( links )
-            output = output + "(%i expected)" % int( numolink )
-            if logLevel == "report":
-                main.log.report( output )
-            elif logLevel == "warn":
-                main.log.warn( output )
-            else:
-                main.log.info( output )
-            return result
-        except pexpect.EOF:
-            main.log.error( self.name + ": EOF exception found" )
-            main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanup()
-            main.exit()
-        except Exception:
-            main.log.exception( self.name + ": Uncaught exception!" )
-            main.cleanup()
-            main.exit()
-
     def tsharkPcap( self, interface, dirFile ):
         """
         Capture all packet activity and store in specified
@@ -1609,6 +1522,54 @@
             main.cleanup()
             main.exit()
 
+    def dumpFlows(self,ONOSIp, destDir, filename="flows" ):
+        """
+        Dump Flow Tables to a desired directory.
+        For debugging purposes, you may want to use
+        this function to capture flows at a given point in time.
+        Localtime will be attached to the filename
+
+        Required:
+            * ONOSIp: the IP of the target ONOS instance
+            * destDir: specify directory to copy to.
+              ex ) /tmp/
+        Optional:
+            * fileName: Name of the file
+        """
+
+        localtime = time.strftime( '%x %X' )
+        localtime = localtime.replace( "/", "" )
+        localtime = localtime.replace( " ", "_" )
+        localtime = localtime.replace( ":", "" )
+        if destDir[ -1: ] != "/":
+            destDir += "/"
+        cmd="flows > "+ str( destDir ) + str( filename ) + localtime
+        return self.onosCli(ONOSIp,cmd)
+
+    def dumpGroups(self,ONOSIp, destDir, filename="groups" ):
+        """
+        Dump Group Tables to a desired directory.
+        For debugging purposes, you may want to use
+        this function to capture groups at a given point in time.
+        Localtime will be attached to the filename
+
+        Required:
+            * ONOSIp: the IP of the target ONOS instance
+            * destDir: specify directory to copy to.
+              ex ) /tmp/
+        Optional:
+            * fileName: Name of the file
+        """
+
+        localtime = time.strftime( '%H %M' )
+        localtime = localtime.replace( "/", "" )
+        localtime = localtime.replace( " ", "_" )
+        localtime = localtime.replace( ":", "" )
+        if destDir[ -1: ] != "/":
+            destDir += "/"
+        cmd="groups > "+ str( destDir ) + str( filename ) + localtime
+        return self.onosCli(ONOSIp,cmd)
+
     def cpLogsToDir( self, logToCopy,
                      destDir, copyFileName="" ):
         """
@@ -1634,7 +1595,7 @@
               with multiple file copying
         """
         try:
-            localtime = time.strftime( '%x %X' )
+            localtime = time.strftime( '%H %M' )
             localtime = localtime.replace( "/", "" )
             localtime = localtime.replace( " ", "_" )
             localtime = localtime.replace( ":", "" )
@@ -1703,6 +1664,8 @@
                 "stop/waiting",
                 "Not Running ...",
                 pexpect.TIMEOUT ], timeout=120 )
+            self.handle.sendline( "" )
+            self.handle.expect( "\$" )
 
             if i == 0 or i == 1:
                 main.log.info( "ONOS is running" )
@@ -2269,7 +2232,7 @@
 
         return passed
 
-    def getIpAddr( self ):
+    def getIpAddr( self, iface=None ):
         """
         Update self.ip_address with numerical ip address. If multiple IP's are
         located on the device, will attempt to use self.nicAddr to choose the
@@ -2279,7 +2242,7 @@
         ONLY WORKS WITH IPV4 ADDRESSES
         """
         try:
-            localhost = "127.0.0.1"
+            LOCALHOST = "127.0.0.1"
             ipPat = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
             pattern = re.compile( ipPat )
             match = re.search( pattern, self.ip_address )
@@ -2296,22 +2259,76 @@
                     nicMatch = re.search( nicPat, curIp )
                     if nicMatch:
                         return self.ip_address
-            # ELSE: attempt to get correct address.
-            raw = subprocess.check_output( "ifconfig")
+            # ELSE: IF iface, return ip of interface
+            cmd = "ifconfig"
+            ifPat = re.compile( "inet addr:({})".format( ipPat ) )
+            if iface:
+                cmd += " " + str( iface )
+            raw = subprocess.check_output( cmd.split() )
             ifPat = re.compile( "inet addr:({})".format( ipPat ) )
             ips = re.findall( ifPat, raw )
+            if iface:
+                if ips:
+                    ip = ips[0]
+                    self.ip_address = ip
+                    return ip
+                else:
+                    main.log.error( "Error finding ip, ifconfig output:".format( raw ) )
+            # ELSE: attempt to get address matching nicPat.
             if nicPat:
                 for ip in ips:
                     curMatch = re.search( nicPat, ip )
                     if curMatch:
                         self.ip_address = ip
                         return ip
-            else:
-                tmpList = [ ip for ip in ips if ip is not localhost ]
+            else:  # If only one non-localhost ip, return that
+                tmpList = [ ip for ip in ips if ip is not LOCALHOST ]
                 if len(tmpList) == 1:
                     curIp = tmpList[0]
                     self.ip_address = curIp
                     return curIp
-            return localhost
+            # Either no non-localhost IPs, or more than 1
+            main.log.warn( "getIpAddr failed to find a public IP address" )
+            return LOCALHOST
+        except subprocess.CalledProcessError:
+            main.log.exception( "Error executing ifconfig" )
+        except IndexError:
+            main.log.exception( "Error getting IP Address" )
         except Exception:
             main.log.exception( "Uncaught exception" )
+
+    def startBasicONOS(self, nodeList, opSleep = 60, onosStartupSleep = 60):
+
+        '''
+        Start onos cluster with defined nodes, but only with drivers app
+
+        '''
+        import time
+
+        self.createCellFile( self.ip_address,
+                                       "temp",
+                                       self.ip_address,
+                                       "drivers",
+                                       nodeList )
+
+        main.log.info( self.name + ": Apply cell to environment" )
+        cellResult = self.setCell( "temp" )
+        verifyResult = self.verifyCell()
+
+        main.log.info( self.name + ": Creating ONOS package" )
+        packageResult = self.onosPackage( opTimeout=opSleep )
+
+        main.log.info( self.name + ": Installing ONOS package" )
+        for nd in nodeList:
+                    self.onosInstall( node=nd )
+
+        main.log.info( self.name + ": Starting ONOS service" )
+        time.sleep( onosStartupSleep )
+
+        onosStatus = True
+        for nd in nodeList:
+            onosStatus = onosStatus & self.isup( node = nd )
+            #print "onosStatus is: " + str( onosStatus )
+
+        return main.TRUE if onosStatus else main.FALSE
+
diff --git a/TestON/logs/.gitignore b/TestON/logs/.gitignore
new file mode 100644
index 0000000..5e7d273
--- /dev/null
+++ b/TestON/logs/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/TestON/logs/placeHolder.txt b/TestON/logs/placeHolder.txt
deleted file mode 100644
index 3b18e51..0000000
--- a/TestON/logs/placeHolder.txt
+++ /dev/null
@@ -1 +0,0 @@
-hello world
diff --git a/TestON/tests/CHO/CHOtest/CHOtest.params b/TestON/tests/CHO/CHOtest/CHOtest.params
index 45d0fef..9cf0709 100644
--- a/TestON/tests/CHO/CHOtest/CHOtest.params
+++ b/TestON/tests/CHO/CHOtest/CHOtest.params
@@ -18,7 +18,7 @@
     # 19X. IPv6 ping across Point,Multi-single,Single-Multi Intents
 
     <testcases>
-    1,21,3,48,148,[5,61,161,72,172,82,182,10,5,91,191,73,173,83,183,10]*20,200,20,3,47,147,[5,60,160,70,170,80,180,10,5,90,190,71,171,81,181,10]*20,200,22,3,49,149,[5,62,162,74,174,84,184,10,5,92,192,75,175,85,185,10]*20
+    1,20,3,47,147,[5,60,160,70,170,80,180,10,5,90,190,71,171,81,181,10]*500
     </testcases>
 
     <DEPENDENCY>
diff --git a/TestON/tests/CHO/CHOtest/CHOtest.py b/TestON/tests/CHO/CHOtest/CHOtest.py
index a1fb04f..8c92c20 100644
--- a/TestON/tests/CHO/CHOtest/CHOtest.py
+++ b/TestON/tests/CHO/CHOtest/CHOtest.py
@@ -243,7 +243,7 @@
         time.sleep( 5 )
 
         topology_output = main.ONOScli1.topology()
-        topology_result = main.ONOSbench.getTopology( topology_output )
+        topology_result = main.ONOScli1.getTopology( topology_output )
         case2Result = ( switch_mastership and startStatus )
         utilities.assert_equals(
             expect=main.TRUE,
@@ -396,7 +396,7 @@
         main.case( "Collect and Store Topology Details from ONOS" )
         main.step( "Collect and store current number of switches and links" )
         topology_output = main.ONOScli1.topology()
-        topology_result = main.ONOSbench.getTopology( topology_output )
+        topology_result = main.ONOScli1.getTopology( topology_output )
         numOnosDevices = topology_result[ 'devices' ]
         numOnosLinks = topology_result[ 'links' ]
         topoResult = main.TRUE
diff --git a/TestON/tests/CHO/CHOtest/dependencies/CHOtestFunctions.py b/TestON/tests/CHO/CHOtest/dependencies/CHOtestFunctions.py
index 7441a5b..c8d40ac 100644
--- a/TestON/tests/CHO/CHOtest/dependencies/CHOtestFunctions.py
+++ b/TestON/tests/CHO/CHOtest/dependencies/CHOtestFunctions.py
@@ -140,7 +140,7 @@
         for e in range( int( main.numCtrls ) ):
             main.log.info( "Checking link number on ONOS%s" % (e+1) )
             topology_output = main.CLIs[e].topology()
-            linkResultIndividual = main.ONOSbench.checkStatus( topology_output,
+            linkResultIndividual = main.ONOScli1.checkStatus( topology_output,
                                                                main.numMNswitches,
                                                                str( linkNum ) )
             if not linkResultIndividual:
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
new file mode 100644
index 0000000..c3a7346
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
@@ -0,0 +1,388 @@
+<PARAMS>
+    # 0. Initialize CHOTestMonkey
+    # 1. Load topology and balances all switches
+    # 2. Collect and store device and link data from ONOS
+    # 3. Collect and store host data from ONOS
+    # 10. Run all enabled checks
+    # 20. Bring down/up links and check topology and ping
+    # 21. Bring down/up a group of links and check topology and ping
+    # 30. Install host intents and check intent states and ping
+    # 31. Uninstall host intents and check intent states
+    # 32. Install point intents and check intent states and ping
+    # 33. Uninstall point intents and check intent states
+    # 40. Randomly bring down one ONOS node
+    # 41. Randomly bring up one ONOS node that is down
+    # 50. Set FlowObjective to True
+    # 51. Set FlowObjective to False
+    # 60. Rebalance devices across controllers
+    # 90. Sleep for some time
+    # 100. Do something else
+    # Sample sequence: 0,1,2,3,[10,30,21,31,10,32,21,33,50,10,30,21,31,10,32,21,33,51,40,60,10,30,21,31,10,32,21,33,50,10,30,21,31,10,32,21,33,51,41,60]*500,100
+    <testcases>
+        0,1,2,3,10,[30,21,31,32,21,33,50,30,21,31,32,21,33,51]*500,100
+    </testcases>
+
+    <TEST>
+        <topo>1</topo>
+        <IPv6>on</IPv6>
+        <numCtrl>3</numCtrl>
+        <pauseTest>on</pauseTest>
+        <caseSleep>0</caseSleep>
+        <setIPv6CfgSleep>5</setIPv6CfgSleep>
+        <loadTopoSleep>5</loadTopoSleep>
+        <ipv6Prefix>1000::</ipv6Prefix>
+        <ipv4Prefix>10.1.</ipv4Prefix>
+        <karafCliTimeout>7200000</karafCliTimeout>
+        <testDuration>86400</testDuration>
+        <package>off</package>
+        <autoPull>off</autoPull>
+        <branch>master</branch>
+    </TEST>
+
+    <EVENT>
+        <Event>
+            <status>on</status>
+            <typeIndex>0</typeIndex>
+            <typeString>NULL</typeString>
+            <CLI>null</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </Event>
+
+        <TestPause>
+            <status>on</status>
+            <typeIndex>1</typeIndex>
+            <typeString>TEST_PAUSE</typeString>
+            <CLI>pause-test</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </TestPause>
+
+        <TestResume>
+            <status>on</status>
+            <typeIndex>2</typeIndex>
+            <typeString>TEST_RESUME</typeString>
+            <CLI>resume-test</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </TestResume>
+
+        <TestSleep>
+            <status>on</status>
+            <typeIndex>3</typeIndex>
+            <typeString>TEST_SLEEP</typeString>
+            <CLI>sleep</CLI>
+            <CLIParamNum>1</CLIParamNum>
+        </TestSleep>
+
+        <IntentCheck>
+            <status>on</status>
+            <typeIndex>10</typeIndex>
+            <typeString>CHECK_INTENT</typeString>
+            <CLI>check-intent</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </IntentCheck>
+
+        <FlowCheck>
+            <status>on</status>
+            <typeIndex>11</typeIndex>
+            <typeString>CHECK_FLOW</typeString>
+            <CLI>check-flow</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </FlowCheck>
+
+        <TrafficCheck>
+            <status>on</status>
+            <typeIndex>12</typeIndex>
+            <typeString>CHECK_TRAFFIC</typeString>
+            <CLI>check-traffic</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+            <pingWait>1</pingWait>
+            <pingTimeout>10</pingTimeout>
+        </TrafficCheck>
+
+        <TopoCheck>
+            <status>on</status>
+            <typeIndex>13</typeIndex>
+            <typeString>CHECK_TOPO</typeString>
+            <CLI>check-topo</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </TopoCheck>
+
+        <ONOSCheck>
+            <status>on</status>
+            <typeIndex>14</typeIndex>
+            <typeString>CHECK_ONOS</typeString>
+            <CLI>check-onos</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </ONOSCheck>
+
+        <LinkDown>
+            <status>on</status>
+            <typeIndex>20</typeIndex>
+            <typeString>NETWORK_LINK_DOWN</typeString>
+            <CLI>link-down</CLI>
+            <CLIParamNum>2</CLIParamNum>
+        </LinkDown>
+
+        <LinkUp>
+            <status>on</status>
+            <typeIndex>21</typeIndex>
+            <typeString>NETWORK_LINK_UP</typeString>
+            <CLI>link-up</CLI>
+            <CLIParamNum>2</CLIParamNum>
+        </LinkUp>
+
+        <DeviceDown>
+            <status>on</status>
+            <typeIndex>22</typeIndex>
+            <typeString>NETWORK_DEVICE_DOWN</typeString>
+            <CLI>device-down</CLI>
+            <CLIParamNum>1</CLIParamNum>
+        </DeviceDown>
+
+        <DeviceUp>
+            <status>on</status>
+            <typeIndex>23</typeIndex>
+            <typeString>NETWORK_DEVICE_UP</typeString>
+            <CLI>device-up</CLI>
+            <CLIParamNum>1</CLIParamNum>
+        </DeviceUp>
+
+        <AddHostIntent>
+            <status>on</status>
+            <typeIndex>30</typeIndex>
+            <typeString>APP_INTENT_HOST_ADD</typeString>
+            <CLI>add-host-intent</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </AddHostIntent>
+
+        <DelHostIntent>
+            <status>on</status>
+            <typeIndex>31</typeIndex>
+            <typeString>APP_INTENT_HOST_DEL</typeString>
+            <CLI>del-host-intent</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </DelHostIntent>
+
+        <AddPointIntent>
+            <status>on</status>
+            <typeIndex>32</typeIndex>
+            <typeString>APP_INTENT_POINT_ADD</typeString>
+            <CLI>add-point-intent</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </AddPointIntent>
+
+        <DelPointIntent>
+            <status>on</status>
+            <typeIndex>33</typeIndex>
+            <typeString>APP_INTENT_POINT_DEL</typeString>
+            <CLI>del-point-intent</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </DelPointIntent>
+
+        <ONOSDown>
+            <status>on</status>
+            <typeIndex>40</typeIndex>
+            <typeString>ONOS_ONOS_DOWN</typeString>
+            <CLI>onos-down</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </ONOSDown>
+
+        <ONOSUp>
+            <status>on</status>
+            <typeIndex>41</typeIndex>
+            <typeString>ONOS_ONOS_UP</typeString>
+            <CLI>onos-up</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </ONOSUp>
+
+        <SetCfg>
+            <status>on</status>
+            <typeIndex>42</typeIndex>
+            <typeString>ONOS_SET_CFG</typeString>
+            <CLI>set-cfg</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </SetCfg>
+
+        <SetFlowObj>
+            <status>on</status>
+            <typeIndex>43</typeIndex>
+            <typeString>ONOS_SET_FLOWOBJ</typeString>
+            <CLI>set-flowobj</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </SetFlowObj>
+
+        <BalanceMasters>
+            <status>on</status>
+            <typeIndex>44</typeIndex>
+            <typeString>ONOS_BALANCE_MASTERS</typeString>
+            <CLI>balance-masters</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>5</maxRerunNum>
+        </BalanceMasters>
+
+        <addAllChecks>
+            <status>on</status>
+            <typeIndex>110</typeIndex>
+            <typeString>CHECK_ALL</typeString>
+            <CLI>check-all</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </addAllChecks>
+
+        <randomLinkToggle>
+            <status>on</status>
+            <typeIndex>120</typeIndex>
+            <typeString>NETWORK_LINK_RANDOM_TOGGLE</typeString>
+            <CLI>link-toggle-random</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </randomLinkToggle>
+
+        <randomLinkGroupToggle>
+            <status>on</status>
+            <typeIndex>121</typeIndex>
+            <typeString>NETWORK_LINK_GROUP_RANDOM_TOGGLE</typeString>
+            <CLI>link-group-toggle-random</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </randomLinkGroupToggle>
+
+        <randomDeviceToggle>
+            <status>on</status>
+            <typeIndex>122</typeIndex>
+            <typeString>NETWORK_DEVICE_RANDOM_TOGGLE</typeString>
+            <CLI>device-toggle-random</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </randomDeviceToggle>
+
+        <randomDeviceGroupToggle>
+            <status>on</status>
+            <typeIndex>123</typeIndex>
+            <typeString>NETWORK_DEVICE_GROUP_RANDOM_TOGGLE</typeString>
+            <CLI>device-group-toggle-random</CLI>
+            <CLIParamNum>3</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </randomDeviceGroupToggle>
+
+        <installAllHostIntents>
+            <status>on</status>
+            <typeIndex>130</typeIndex>
+            <typeString>APP_INTENT_HOST_ADD_ALL</typeString>
+            <CLI>add-all-host-intents</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </installAllHostIntents>
+
+        <removeAllHostIntents>
+            <status>on</status>
+            <typeIndex>131</typeIndex>
+            <typeString>APP_INTENT_HOST_DEL_ALL</typeString>
+            <CLI>del-all-host-intents</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <sleepBeforeCheck>5</sleepBeforeCheck>
+        </removeAllHostIntents>
+
+        <installAllPointIntents>
+            <status>on</status>
+            <typeIndex>132</typeIndex>
+            <typeString>APP_INTENT_POINT_ADD_ALL</typeString>
+            <CLI>add-all-point-intents</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </installAllPointIntents>
+
+        <removeAllPointIntents>
+            <status>on</status>
+            <typeIndex>133</typeIndex>
+            <typeString>APP_INTENT_POINT_DEL_ALL</typeString>
+            <CLI>del-all-point-intents</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <sleepBeforeCheck>5</sleepBeforeCheck>
+        </removeAllPointIntents>
+
+        <randomONOSToggle>
+            <status>on</status>
+            <typeIndex>140</typeIndex>
+            <typeString>ONOS_ONOS_RANDOM_TOGGLE</typeString>
+            <CLI>onos-toggle-random</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <sleepBeforeCheck>10</sleepBeforeCheck>
+        </randomONOSToggle>
+    </EVENT>
+
+    <SCHEDULER>
+        <pendingEventsCapacity>1</pendingEventsCapacity>
+        <runningEventsCapacity>10</runningEventsCapacity>
+        <scheduleLoopSleep>0.1</scheduleLoopSleep>
+    </SCHEDULER>
+
+    <GENERATOR>
+        <listenerPort>6000</listenerPort>
+        <insertEventRetryInterval>1</insertEventRetryInterval>
+    </GENERATOR>
+
+    <TOPO>
+        <topo0>
+            <fileName>topoTripleIpv6.py</fileName>
+        </topo0>
+
+        <topo1>
+            <fileName>topoAttIpv6.py</fileName>
+        </topo1>
+
+        <topo2>
+            <fileName>topoChordalIpv6.py</fileName>
+        </topo2>
+
+        <topo3>
+            <fileName>topoSpineIpv6.py</fileName>
+        </topo3>
+
+        <topo4>
+            <fileName>topoRingIpv6.py</fileName>
+        </topo4>
+    </TOPO>
+
+    <CASE20>
+        <linkToggleNum>5</linkToggleNum>
+        <linkDownUpInterval>1</linkDownUpInterval>
+    </CASE20>
+
+    <CASE21>
+        <linkGroupSize>5</linkGroupSize>
+        <linkDownDownInterval>1</linkDownDownInterval>
+        <linkDownUpInterval>1</linkDownUpInterval>
+    </CASE21>
+
+    <CASE90>
+        <sleepSec>60</sleepSec>
+    </CASE90>
+</PARAMS>
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
new file mode 100644
index 0000000..314bde9
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
@@ -0,0 +1,806 @@
+"""
+CHOTestMonkey class
+Author: you@onlab.us
+"""
+
+import sys
+import os
+import re
+import time
+import json
+import itertools
+
+class CHOTestMonkey:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE0( self, main ):
+        """
+        Startup sequence:
+        apply cell <name>
+        git pull
+        mvn clean install
+        onos-package
+        onos-verify-cell
+        onos-uninstall
+        onos-install
+        onos-start-cli
+        Set IPv6 cfg parameters for Neighbor Discovery
+        start event scheduler
+        start event listener
+        """
+        import time
+        from threading import Lock, Condition
+        from tests.CHOTestMonkey.dependencies.elements.ONOSElement import Controller
+        from tests.CHOTestMonkey.dependencies.EventGenerator import EventGenerator
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduler
+
+        gitPull = main.params[ 'TEST' ][ 'autoPull' ]
+        onosPackage = main.params[ 'TEST' ][ 'package' ]
+        gitBranch = main.params[ 'TEST' ][ 'branch' ]
+        karafTimeout = main.params[ 'TEST' ][ 'karafCliTimeout' ]
+        main.enableIPv6 = main.params[ 'TEST' ][ 'IPv6' ]
+        main.enableIPv6 = True if main.enableIPv6 == "on" else False
+        main.caseSleep = int( main.params[ 'TEST' ][ 'caseSleep' ] )
+        main.numCtrls = main.params[ 'TEST' ][ 'numCtrl' ]
+        main.controllers = []
+        for i in range( 1, int( main.numCtrls ) + 1 ):
+            newController = Controller( i )
+            newController.setCLI( getattr( main, 'ONOScli' + str( i ) ) )
+            main.controllers.append( newController )
+        main.devices = []
+        main.links = []
+        main.hosts = []
+        main.intents = []
+        main.enabledEvents = {}
+        for eventName in main.params[ 'EVENT' ].keys():
+            if main.params[ 'EVENT' ][ eventName ][ 'status' ] == 'on':
+                main.enabledEvents[ int( main.params[ 'EVENT' ][ eventName ][ 'typeIndex' ] ) ] = eventName
+        print main.enabledEvents
+        main.eventScheduler = EventScheduler()
+        main.eventGenerator = EventGenerator()
+        main.variableLock = Lock()
+        main.mininetLock = Lock()
+        main.ONOSbenchLock = Lock()
+        main.threadID = 0
+        main.eventID = 0
+        main.caseResult = main.TRUE
+
+        main.case( "Set up test environment" )
+        main.log.report( "Set up test environment" )
+        main.log.report( "_______________________" )
+
+        main.step( "Apply Cell environment for ONOS" )
+        if ( main.onoscell ):
+            cellName = main.onoscell
+            cellResult = main.ONOSbench.setCell( cellName )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=cellResult,
+                                     onpass="Test step PASS",
+                                     onfail="Test step FAIL" )
+        else:
+            main.log.error( "Please provide onoscell option at TestON CLI to run CHO tests" )
+            main.log.error( "Example: ~/TestON/bin/cli.py run CHOTestMonkey onoscell <cellName>" )
+            main.cleanup()
+            main.exit()
+
+        main.step( "Git checkout and pull " + gitBranch )
+        if gitPull == 'on':
+            checkoutResult = main.ONOSbench.gitCheckout( gitBranch )
+            pullResult = main.ONOSbench.gitPull()
+            cpResult = ( checkoutResult and pullResult )
+        else:
+            checkoutResult = main.TRUE
+            pullResult = main.TRUE
+            main.log.info( "Skipped git checkout and pull as they are disabled in params file" )
+            cpResult = ( checkoutResult and pullResult )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cpResult,
+                                 onpass="Test step PASS",
+                                 onfail="Test step FAIL" )
+
+        main.step( "mvn clean & install" )
+        if gitPull == 'on':
+            mvnResult = main.ONOSbench.cleanInstall()
+        else:
+            mvnResult = main.TRUE
+            main.log.info( "Skipped mvn clean install as it is disabled in params file" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=mvnResult,
+                                 onpass="Test step PASS",
+                                 onfail="Test step FAIL" )
+        main.ONOSbench.getVersion( report=True )
+
+        main.step( "Create ONOS package" )
+        if onosPackage == 'on':
+            packageResult = main.ONOSbench.onosPackage()
+        else:
+            packageResult = main.TRUE
+            main.log.info( "Skipped onos package as it is disabled in params file" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=packageResult,
+                                 onpass="Test step PASS",
+                                 onfail="Test step FAIL" )
+
+        main.step( "Uninstall ONOS package on all Nodes" )
+        uninstallResult = main.TRUE
+        for i in range( int( main.numCtrls ) ):
+            main.log.info( "Uninstalling package on ONOS Node IP: " + main.onosIPs[i] )
+            uResult = main.ONOSbench.onosUninstall( main.onosIPs[i] )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=uResult,
+                                     onpass="Test step PASS",
+                                     onfail="Test step FAIL" )
+            uninstallResult = ( uninstallResult and uResult )
+
+        main.step( "Install ONOS package on all Nodes" )
+        installResult = main.TRUE
+        for i in range( int( main.numCtrls ) ):
+            main.log.info( "Installing package on ONOS Node IP: " + main.onosIPs[i] )
+            iResult = main.ONOSbench.onosInstall( node=main.onosIPs[i] )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=iResult,
+                                     onpass="Test step PASS",
+                                     onfail="Test step FAIL" )
+            installResult = ( installResult and iResult )
+
+        main.step( "Start ONOS CLI on all nodes" )
+        cliResult = main.TRUE
+        startCliResult  = main.TRUE
+        pool = []
+        for controller in main.controllers:
+            t = main.Thread( target=controller.startCLI,
+                             threadID=main.threadID,
+                             name="startOnosCli",
+                             args=[ ] )
+            pool.append(t)
+            t.start()
+            main.threadID = main.threadID + 1
+        for t in pool:
+            t.join()
+            startCliResult = startCliResult and t.result
+        if not startCliResult:
+            main.log.info( "ONOS CLI did not start up properly" )
+            main.cleanup()
+            main.exit()
+        else:
+            main.log.info( "Successful CLI startup" )
+            startCliResult = main.TRUE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=startCliResult,
+                                 onpass="Test step PASS",
+                                 onfail="Test step FAIL" )
+
+        main.step( "Set IPv6 cfg parameters for Neighbor Discovery" )
+        setIPv6CfgSleep = int( main.params[ 'TEST' ][ 'setIPv6CfgSleep' ] )
+        if main.enableIPv6:
+            time.sleep( setIPv6CfgSleep )
+            cfgResult1 = main.controllers[ 0 ].CLI.setCfg( "org.onosproject.proxyarp.ProxyArp",
+                                                           "ipv6NeighborDiscovery",
+                                                           "true" )
+            time.sleep( setIPv6CfgSleep )
+            cfgResult2 = main.controllers[ 0 ].CLI.setCfg( "org.onosproject.provider.host.impl.HostLocationProvider",
+                                                           "ipv6NeighborDiscovery",
+                                                           "true" )
+        else:
+            main.log.info( "Skipped setting IPv6 cfg parameters as it is disabled in params file" )
+            cfgResult1 = main.TRUE
+            cfgResult2 = main.TRUE
+        cfgResult = cfgResult1 and cfgResult2
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cfgResult,
+                                 onpass="ipv6NeighborDiscovery cfg is set to true",
+                                 onfail="Failed to cfg set ipv6NeighborDiscovery" )
+
+        main.step( "Start a thread for the scheduler" )
+        t = main.Thread( target=main.eventScheduler.startScheduler,
+                         threadID=main.threadID,
+                         name="startScheduler",
+                         args=[] )
+        t.start()
+        stepResult = main.TRUE
+        with main.variableLock:
+            main.threadID = main.threadID + 1
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Test step PASS",
+                                 onfail="Test step FAIL" )
+
+        main.step( "Start a thread to listen to and handle network, ONOS and application events" )
+        t = main.Thread( target=main.eventGenerator.startListener,
+                         threadID=main.threadID,
+                         name="startListener",
+                         args=[] )
+        t.start()
+        with main.variableLock:
+            main.threadID = main.threadID + 1
+
+        caseResult = installResult and uninstallResult and startCliResult and cfgResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=caseResult,
+                                 onpass="Set up test environment PASS",
+                                 onfail="Set up test environment FAIL" )
+
+    def CASE1( self, main ):
+        """
+        Load Mininet topology and balances all switches
+        """
+        import re
+        import time
+        import copy
+
+        main.topoIndex = "topo" + str ( main.params[ 'TEST' ][ 'topo' ] )
+
+        main.log.report( "Load Mininet topology and Balance all Mininet switches across controllers" )
+        main.log.report( "________________________________________________________________________" )
+        main.case( "Assign and Balance all Mininet switches across controllers" )
+
+        main.step( "Start Mininet topology" )
+        newTopo = main.params[ 'TOPO' ][ main.topoIndex ][ 'fileName' ]
+        mininetDir = main.Mininet1.home + "/custom/"
+        topoPath = main.testDir + "/" + main.TEST  + "/dependencies/topologies/" + newTopo
+        main.ONOSbench.secureCopy( main.Mininet1.user_name, main.Mininet1.ip_address, topoPath, mininetDir, direction="to" )
+        topoPath = mininetDir + newTopo
+        startStatus = main.Mininet1.startNet( topoFile = topoPath )
+        main.mininetSwitches = main.Mininet1.getSwitches()
+        main.mininetHosts = main.Mininet1.getHosts()
+        main.mininetLinks = main.Mininet1.getLinks()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=startStatus,
+                                 onpass="Start Mininet topology test PASS",
+                                 onfail="Start Mininet topology test FAIL" )
+
+        main.step( "Assign switches to controllers" )
+        switchMastership = main.TRUE
+        for switchName in main.mininetSwitches.keys():
+            main.Mininet1.assignSwController( sw=switchName, ip=main.onosIPs )
+            response = main.Mininet1.getSwController( switchName )
+            print( "Response is " + str( response ) )
+            if re.search( "tcp:" + main.onosIPs[ 0 ], response ):
+                switchMastership = switchMastership and main.TRUE
+            else:
+                switchMastership = main.FALSE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=switchMastership,
+                                 onpass="Assign switches to controllers test PASS",
+                                 onfail="Assign switches to controllers test FAIL" )
+        # Waiting here to make sure topology converges across all nodes
+        sleep = int( main.params[ 'TEST' ][ 'loadTopoSleep' ] )
+        time.sleep( sleep )
+
+        main.step( "Balance devices across controllers" )
+        balanceResult = main.ONOScli1.balanceMasters()
+        # giving some breathing time for ONOS to complete re-balance
+        time.sleep( sleep )
+
+        caseResult = ( startStatus and switchMastership and balanceResult )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=caseResult,
+                                 onpass="Starting new Att topology test PASS",
+                                 onfail="Starting new Att topology test FAIL" )
+
+    def CASE2( self, main ):
+        """
+        Collect and store device and link data from ONOS
+        """
+        import json
+        from tests.CHOTestMonkey.dependencies.elements.NetworkElement import Device, Link
+
+        main.log.report( "Collect and Store topology details from ONOS" )
+        main.log.report( "____________________________________________________________________" )
+        main.case( "Collect and Store Topology Details from ONOS" )
+        topoResult = main.TRUE
+        topologyOutput = main.ONOScli1.topology()
+        topologyResult = main.ONOScli1.getTopology( topologyOutput )
+        ONOSDeviceNum = int( topologyResult[ 'devices' ] )
+        ONOSLinkNum = int( topologyResult[ 'links' ] )
+        mininetSwitchNum = len( main.mininetSwitches )
+        mininetLinkNum = ( len( main.mininetLinks ) - len( main.mininetHosts ) ) * 2
+        if mininetSwitchNum == ONOSDeviceNum and mininetLinkNum == ONOSLinkNum:
+            main.step( "Collect and store device data" )
+            stepResult = main.TRUE
+            dpidToName = {}
+            for key, value in main.mininetSwitches.items():
+                dpidToName[ 'of:' + str( value[ 'dpid' ] ) ] = key
+            devicesRaw = main.ONOScli1.devices()
+            devices = json.loads( devicesRaw )
+            deviceInitIndex = 0
+            for device in devices:
+                name = dpidToName[ device[ 'id' ] ]
+                newDevice = Device( deviceInitIndex, name, device[ 'id' ] )
+                print newDevice
+                main.devices.append( newDevice )
+                deviceInitIndex += 1
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully collected and stored device data",
+                                     onfail="Failed to collect and store device data" )
+
+            main.step( "Collect and store link data" )
+            stepResult = main.TRUE
+            linksRaw = main.ONOScli1.links()
+            links = json.loads( linksRaw )
+            linkInitIndex = 0
+            for link in links:
+                for device in main.devices:
+                    if device.dpid == link[ 'src' ][ 'device' ]:
+                        deviceA = device
+                    elif device.dpid == link[ 'dst' ][ 'device' ]:
+                        deviceB = device
+                assert deviceA != None and deviceB != None
+                newLink = Link( linkInitIndex, deviceA, link[ 'src' ][ 'port' ], deviceB, link[ 'dst' ][ 'port' ] )
+                print newLink
+                main.links.append( newLink )
+                linkInitIndex += 1
+            # Set backward links and outgoing links of devices
+            for linkA in main.links:
+                linkA.deviceA.outgoingLinks.append( linkA )
+                if linkA.backwardLink != None:
+                    continue
+                for linkB in main.links:
+                    if linkB.backwardLink != None:
+                        continue
+                    if linkA.deviceA == linkB.deviceB and\
+                    linkA.deviceB == linkB.deviceA and\
+                    linkA.portA == linkB.portB and\
+                    linkA.portB == linkB.portA:
+                        linkA.setBackwardLink( linkB )
+                        linkB.setBackwardLink( linkA )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully collected and stored link data",
+                                     onfail="Failed to collect and store link data" )
+        else:
+            main.log.info( "Devices (expected): %s, Links (expected): %s" % ( mininetSwitchNum, mininetLinkNum ) )
+            main.log.info( "Devices (actual): %s, Links (actual): %s" % ( ONOSDeviceNum, ONOSLinkNum ) )
+            topoResult = main.FALSE
+
+        caseResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=caseResult,
+                                 onpass="Saving ONOS topology data test PASS",
+                                 onfail="Saving ONOS topology data test FAIL" )
+
+        if not caseResult:
+            main.log.info("Topology does not match, exiting test...")
+            main.cleanup()
+            main.exit()
+
+    def CASE3( self, main ):
+        """
+        Collect and store host data from ONOS
+        """
+        import json
+        from tests.CHOTestMonkey.dependencies.elements.NetworkElement import Host
+
+        main.log.report( "Collect and store host adta from ONOS" )
+        main.log.report( "______________________________________________" )
+        main.case( "Use fwd app and pingall to discover all the hosts, then collect and store host data" )
+
+        main.step( "Enable Reactive forwarding" )
+        appResult = main.controllers[ 0 ].CLI.activateApp( "org.onosproject.fwd" )
+        cfgResult1 = main.TRUE
+        cfgResult2 = main.TRUE
+        if main.enableIPv6:
+            cfgResult1 = main.controllers[ 0 ].CLI.setCfg( "org.onosproject.fwd.ReactiveForwarding", "ipv6Forwarding", "true" )
+            cfgResult2 = main.controllers[ 0 ].CLI.setCfg( "org.onosproject.fwd.ReactiveForwarding", "matchIpv6Address", "true" )
+        stepResult = appResult and cfgResult1 and cfgResult2
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully enabled reactive forwarding",
+                                 onfail="Failed to enable reactive forwarding" )
+
+        main.step( "Discover hosts using pingall" )
+        stepResult = main.TRUE
+        main.Mininet1.pingall()
+        if main.enableIPv6:
+            ping6Result = main.Mininet1.pingall( protocol="IPv6" )
+        hosts = main.controllers[ 0 ].CLI.hosts()
+        hosts = json.loads( hosts )
+        if not len( hosts ) == len( main.mininetHosts ):
+            stepResult = main.FALSE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Host discovery PASS",
+                                 onfail="Host discovery FAIL" )
+        if not stepResult:
+            main.log.debug( hosts )
+            main.cleanup()
+            main.exit()
+
+        main.step( "Disable Reactive forwarding" )
+        appResult = main.controllers[ 0 ].CLI.deactivateApp( "org.onosproject.fwd" )
+        stepResult = appResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully deactivated fwd app",
+                                 onfail="Failed to deactivate fwd app" )
+
+        main.step( "Collect and store host data" )
+        stepResult = main.TRUE
+        macToName = {}
+        for key, value in main.mininetHosts.items():
+            macToName[ value[ 'interfaces' ][ 0 ][ 'mac' ].upper() ] = key
+        dpidToDevice = {}
+        for device in main.devices:
+            dpidToDevice[ device.dpid ] = device
+        hostInitIndex = 0
+        for host in hosts:
+            name = macToName[ host[ 'mac' ] ]
+            dpid = host[ 'location' ][ 'elementId' ]
+            device = dpidToDevice[ dpid ]
+            newHost = Host( hostInitIndex,
+                            name, host[ 'id' ], host[ 'mac' ],
+                            device, host[ 'location' ][ 'port' ],
+                            host[ 'vlan' ], host[ 'ipAddresses' ] )
+            print newHost
+            main.hosts.append( newHost )
+            main.devices[ device.index ].hosts.append( newHost )
+            hostInitIndex += 1
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully collected and stored host data",
+                                 onfail="Failed to collect and store host data" )
+
+        main.step( "Create one host component for each host and then start host cli" )
+        for host in main.hosts:
+            main.Mininet1.createHostComponent( host.name )
+            hostHandle = getattr( main, host.name )
+            main.log.info( "Starting CLI on host " + str( host.name ) )
+            startCLIResult = hostHandle.startHostCli()
+            host.setHandle( hostHandle )
+            stepResult = startCLIResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=startCLIResult,
+                                     onpass="Host CLI started",
+                                     onfail="Failed to start host CLI" )
+
+    def CASE10( self, main ):
+        """
+        Run all enabled checks
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Run all enabled checks" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Run all enabled checks" )
+        main.step( "Run all enabled checks" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().CHECK_ALL, EventScheduleMethod().RUN_BLOCK )
+        # Wait for the scheduler to become idle before going to the next testcase
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="All enabled checks passed",
+                                 onfail="Not all enabled checks passed" )
+        time.sleep( main.caseSleep )
+
+    def CASE20( self, main ):
+        """
+        Bring down/up links and check topology and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Bring down/up links and check topology and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Bring down/up links and check topology and ping" )
+        main.step( "Bring down/up links and check topology and ping" )
+        main.caseResult = main.TRUE
+        linkToggleNum = int( main.params[ 'CASE20' ][ 'linkToggleNum' ] )
+        linkDownUpInterval = int( main.params[ 'CASE20' ][ 'linkDownUpInterval' ] )
+        for i in range( 0, linkToggleNum ):
+            main.eventGenerator.triggerEvent( EventType().NETWORK_LINK_RANDOM_TOGGLE, EventScheduleMethod().RUN_BLOCK, linkDownUpInterval )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Toggle network links test passed",
+                                 onfail="Toggle network links test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE21( self, main ):
+        """
+        Bring down/up a group of links and check topology and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Bring down/up a group of links and check topology and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Bring down/up a group of links and check topology and ping" )
+        main.step( "Bring down/up a group of links and check topology and ping" )
+        main.caseResult = main.TRUE
+        linkGroupSize = int( main.params[ 'CASE21' ][ 'linkGroupSize' ] )
+        linkDownDownInterval = int( main.params[ 'CASE21' ][ 'linkDownDownInterval' ] )
+        linkDownUpInterval = int( main.params[ 'CASE21' ][ 'linkDownUpInterval' ] )
+        main.eventGenerator.triggerEvent( EventType().NETWORK_LINK_GROUP_RANDOM_TOGGLE, EventScheduleMethod().RUN_BLOCK, linkGroupSize, linkDownDownInterval, linkDownUpInterval )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Toggle network link group test passed",
+                                 onfail="Toggle network link group test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE30( self, main ):
+        """
+        Install host intents and check intent states and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Install host intents and check intent states and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Install host intents and check intent states and ping" )
+        main.step( "Install host intents and check intent states and ping" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().APP_INTENT_HOST_ADD_ALL, EventScheduleMethod().RUN_BLOCK )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Install host intents test passed",
+                                 onfail="Install host intents test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE31( self, main ):
+        """
+        Uninstall host intents and check intent states and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Uninstall host intents and check intent states and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Uninstall host intents and check intent states and ping" )
+        main.step( "Uninstall host intents and check intent states and ping" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().APP_INTENT_HOST_DEL_ALL, EventScheduleMethod().RUN_BLOCK )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Uninstall host intents test passed",
+                                 onfail="Uninstall host intents test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE32( self, main ):
+        """
+        Install point intents and check intent states and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Install point intents and check intent states and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Install point intents and check intent states and ping" )
+        main.step( "Install point intents and check intent states and ping" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_ADD_ALL, EventScheduleMethod().RUN_BLOCK )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Install point intents test passed",
+                                 onfail="Install point intents test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE33( self, main ):
+        """
+        Uninstall point intents and check intent states and ping
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Uninstall point intents and check intent states and ping" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Uninstall point intents and check intent states and ping" )
+        main.step( "Uninstall point intents and check intent states and ping" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_DEL_ALL, EventScheduleMethod().RUN_BLOCK )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Uninstall point intents test passed",
+                                 onfail="Uninstall point intents test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE40( self, main ):
+        """
+        Randomly bring down one ONOS node
+        """
+        import time
+        import random
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Randomly bring down one ONOS node" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Randomly bring down one ONOS node" )
+        main.step( "Randomly bring down one ONOS node" )
+        main.caseResult = main.TRUE
+        availableControllers = []
+        for controller in main.controllers:
+            if controller.isUp():
+                availableControllers.append( controller.index )
+        if len( availableControllers ) == 0:
+            main.log.warn( "No available controllers" )
+            main.caseResult = main.FALSE
+        else:
+            index = random.sample( availableControllers, 1 )
+            main.eventGenerator.triggerEvent( EventType().ONOS_ONOS_DOWN, EventScheduleMethod().RUN_BLOCK, index[ 0 ] )
+            with main.eventScheduler.idleCondition:
+                while not main.eventScheduler.isIdle():
+                    main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Randomly bring down ONOS test passed",
+                                 onfail="Randomly bring down ONOS test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE41( self, main ):
+        """
+        Randomly bring up one ONOS node that is down
+        """
+        import time
+        import random
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Randomly bring up one ONOS node that is down" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Randomly bring up one ONOS node that is down" )
+        main.step( "Randomly bring up one ONOS node that is down" )
+        main.caseResult = main.TRUE
+        targetControllers = []
+        for controller in main.controllers:
+            if not controller.isUp():
+                targetControllers.append( controller.index )
+        if len( targetControllers ) == 0:
+            main.log.warn( "All controllers are up" )
+            main.caseResult = main.FALSE
+        else:
+            index = random.sample( targetControllers, 1 )
+            main.eventGenerator.triggerEvent( EventType().ONOS_ONOS_UP, EventScheduleMethod().RUN_BLOCK, index[ 0 ] )
+            with main.eventScheduler.idleCondition:
+                while not main.eventScheduler.isIdle():
+                    main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Randomly bring up ONOS test passed",
+                                 onfail="Randomly bring up ONOS test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE50( self, main ):
+        """
+        Set FlowObjective to True
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Set FlowObjective to True" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Set FlowObjective to True" )
+        main.step( "Set FlowObjective to True" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().ONOS_SET_FLOWOBJ, EventScheduleMethod().RUN_BLOCK, 'true' )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Set FlowObjective test passed",
+                                 onfail="Set FlowObjective test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE51( self, main ):
+        """
+        Set FlowObjective to False
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Set FlowObjective to False" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Set FlowObjective to False" )
+        main.step( "Set FlowObjective to False" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().ONOS_SET_FLOWOBJ, EventScheduleMethod().RUN_BLOCK, 'false' )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Set FlowObjective test passed",
+                                 onfail="Set FlowObjective test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE60( self, main ):
+        """
+        Balance device masters
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Balance device masters" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Balance device masters" )
+        main.step( "Balance device masters" )
+        main.caseResult = main.TRUE
+        main.eventGenerator.triggerEvent( EventType().ONOS_BALANCE_MASTERS, EventScheduleMethod().RUN_BLOCK )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Balance masters test passed",
+                                 onfail="Balance masters test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE90( self, main ):
+        """
+        Sleep for some time
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Sleep for some time" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Sleep for some time" )
+        main.step( "Sleep for some time" )
+        main.caseResult = main.TRUE
+        sleepSec = int( main.params[ 'CASE90' ][ 'sleepSec' ] )
+        main.eventGenerator.triggerEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, sleepSec )
+        with main.eventScheduler.idleCondition:
+            while not main.eventScheduler.isIdle():
+                main.eventScheduler.idleCondition.wait()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Sleep test passed",
+                                 onfail="Sleep test failed" )
+        time.sleep( main.caseSleep )
+
+    def CASE100( self, main ):
+        """
+        Do something else?
+        """
+        import time
+
+        main.log.report( "Do something else?" )
+        main.log.report( "__________________________________________________" )
+        main.case( "..." )
+
+        main.step( "Wait until the test stops" )
+
+        main.caseResult = main.TRUE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Test PASS",
+                                 onfail="Test FAIL" )
+
+        testDuration = int( main.params[ 'TEST' ][ 'testDuration' ] )
+        time.sleep( testDuration )
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo b/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo
new file mode 100644
index 0000000..7c7cfd7
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo
@@ -0,0 +1,50 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>admin</user>
+            <password></password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOSbench>
+
+        <ONOScli1>
+            <host>localhost</host>
+            <user>admin</user>
+            <password></password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli1>
+
+        <ONOScli2>
+            <host>localhost</host>
+            <user>admin</user>
+            <password></password>
+            <type>OnosCliDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli2>
+
+        <ONOScli3>
+            <host>localhost</host>
+            <user>admin</user>
+            <password></password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli3>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>admin</user>
+            <password></password>
+            <type>MininetCliDriver</type>
+            <connect_order>10</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/CHOTestMonkey/README b/TestON/tests/CHOTestMonkey/README
new file mode 100644
index 0000000..cfd85e7
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/README
@@ -0,0 +1,33 @@
+CHO TEST MONKEY
+
+Summary:
+        This is a long-term regression test that is aimed to run for weeks
+        at a time. It's goal is to find memory leaks or bugs that otherwise
+        cannot be easily seen with short-term tests.
+        CHOTestMonkey is the 2016 version of CHOtest. The suffix "Monkey"
+        implies both the Chaos Monkey style testing and the year of the
+        Monkey 2016.
+
+Topology:
+        Att topology - 25 switches and 25 hosts and its structure is
+                       designed to simulate a real world configuration.
+        Chordal topology - 25 swithces and 25 hosts and because of its
+                           chordal graph structure, it's particulary useful
+                           in testing the rerouting capability of ONOS.
+        Leaf-spine topology - 78 switches and 68 hosts and designed to
+                              simulate modern data centers.
+
+Pre-requisites:
+        To run out-of-the box this test requires 3 NODES. The cell file
+        must be passed through the startup test command, e.g.,
+
+            ./cli.py run CHOTestMonkey onoscell <cell name>.
+
+        Passwordless login must be set from TestStation "sdn" root user.
+        This test relies on the topology files to start Mininet located in
+        the dependencies/topologies folder. Be sure to check that each
+        topology file can be loaded properly by Mininet by using this
+        command:
+
+            sudo ~/dependencies/topologies/<topology name>
+
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/CHOTestMonkey/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/CHOTestMonkey/__init__.py
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py b/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py
new file mode 100644
index 0000000..de32193
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py
@@ -0,0 +1,563 @@
+"""
+This file contains the event generator class for CHOTestMonkey
+Author: you@onlab.us
+"""
+from threading import Lock, Condition
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+from tests.CHOTestMonkey.dependencies.GraphHelper import GraphHelper
+
+class MessageType:
+    def __init__( self ):
+        self.map = {}
+        # This message type is used for requesting an event injection from outside CHOTestMonkey
+        self.EVENT_REQUEST = 1
+        self.map[ 1 ] = 'EVENT_REQUEST'
+        # This message tyoe will force the event generator to accept the event injection request for debugging purpose
+        self.EVENT_REQUEST_DEBUG = 2
+        self.map[ 2 ] = 'EVENT_REQUEST_DEBUG'
+        # This message type implies the event generator has inserted the event
+        self.EVENT_INSERTED = 10
+        self.map[ 10 ] = 'EVENT_INSERTED'
+        # This message type implies CHOTestMonkey has refused the event injection request
+        # due to, e.g. too many pending events in the scheduler
+        self.EVENT_DENIED = 11
+        self.map[ 11 ] = 'EVENT_DENIED'
+        # The followings are error messages
+        self.UNKNOWN_MESSAGE = 20
+        self.map[ 20 ] = 'UNKNOWN_MESSAGE'
+        self.UNKNOWN_EVENT_TYPE = 21
+        self.map[ 21 ] = 'UNKNOWN_EVENT_TYPE'
+        self.UNKNOWN_SCHEDULE_METHOD = 22
+        self.map[ 22 ] = 'UNKNOWN_SCHEDULE_METHOD'
+        self.NOT_ENOUGH_ARGUMENT = 23
+        self.map[ 23 ] = 'NOT_ENOUGH_ARGUMENT'
+
+class EventGenerator:
+    def __init__( self ):
+        self.default = ''
+        self.eventGeneratorLock = Lock()
+
+    def startListener( self ):
+        """
+        Listen to event triggers
+        """
+        from multiprocessing.connection import Listener
+        import time
+
+        host = "localhost"
+        port = int( main.params[ 'GENERATOR' ][ 'listenerPort' ] )
+        address = ( host, port )
+        listener = Listener( address )
+        main.log.info( "Event Generator - Event listener start listening on %s:%s" % ( host, port ) )
+
+        while 1:
+            conn = listener.accept()
+            t = main.Thread( target=self.handleConnection,
+                             threadID=main.threadID,
+                             name="handleConnection",
+                             args=[ conn ])
+            t.start()
+            with main.variableLock:
+                main.threadID += 1
+        listener.close()
+
+    def handleConnection( self, conn ):
+        """
+        Handle connections from event triggers
+        """
+        request = conn.recv()
+        if isinstance( request, list ) and ( request[ 0 ] == MessageType().EVENT_REQUEST or request[ 0 ] == MessageType().EVENT_REQUEST_DEBUG ):
+            if len( request ) < 3:
+                response = MessageType().NOT_ENOUGH_ARGUMENT
+            elif request[ 0 ] == MessageType().EVENT_REQUEST and not main.eventScheduler.isAvailable():
+                response = MessageType().EVENT_DENIED
+            else:
+                typeString = str( request[ 1 ] )
+                scheduleMethodString = str( request[ 2 ] )
+                if len( request ) > 3:
+                    args = request[ 3: ]
+                else:
+                    args = None
+                for key, value in EventType().map.items():
+                    if value == typeString:
+                        typeIndex = key
+                        break
+                if not value == typeString:
+                    response = MessageType().UNKNOWN_EVENT_TYPE
+                else:
+                    for key, value in EventScheduleMethod().map.items():
+                        if value == scheduleMethodString:
+                            scheduleMethod = key
+                            break
+                    if not value == scheduleMethodString:
+                        response = MessageType().UNKNOWN_SCHEDULE_METHOD
+                    else:
+                        self.insertEvent( typeIndex, scheduleMethod, args )
+                        response = MessageType().EVENT_INSERTED
+        else:
+            response = MessageType().UNKNOWN_MESSAGE
+        conn.send( response )
+        conn.close()
+
+    def triggerEvent( self, typeIndex, scheduleMethod, *args ):
+        """
+        This function triggers an event from inside of CHOTestMonkey
+        """
+        import time
+        if not typeIndex in EventType().map.keys():
+            main.log.warn( "Event Generator - Unknown event type: " + str( typeIndex ) )
+            return
+        if not scheduleMethod in EventScheduleMethod().map.keys():
+            main.log.warn( "Event Generator - Unknown event schedule method: " + str( scheduleMethod ) )
+            return
+        while not main.eventScheduler.isAvailable():
+            time.sleep( int( main.params[ 'GENERATOR' ][ 'insertEventRetryInterval' ] ) )
+        self.insertEvent( typeIndex, scheduleMethod, list( args ) )
+
+    def insertEvent( self, typeIndex, scheduleMethod, args=None ):
+        """
+        This function inserts an event into the scheduler
+        """
+        if typeIndex > 100:
+            # Handle group events
+            if not typeIndex in main.enabledEvents.keys():
+                main.log.warn( "Event Generator - event type %s not enabled" % ( typeIndex ) )
+                return
+            function = getattr( self, main.enabledEvents[ typeIndex ] )
+            assert function != None, "Event Generator - funtion for group event " + typeIndex + " not found"
+            function( scheduleMethod, args )
+        else:
+            # Add individual events to the scheduler
+            main.eventScheduler.scheduleEvent( typeIndex, scheduleMethod, args )
+
+    def insertAllChecks( self, args=None ):
+        """
+        Acquire eventGeneratorLock before calling this funtion
+        """
+        for eventType in main.enabledEvents.keys():
+            if eventType < 100 and EventType().map[ eventType ].startswith( 'CHECK' ):
+                main.eventScheduler.scheduleEvent( eventType,
+                                                   EventScheduleMethod().RUN_NON_BLOCK,
+                                                   args )
+
+    def addAllChecks( self, scheduleMethod, args=None ):
+        """
+        The function adds all check events into the scheduler
+        """
+        with self.eventGeneratorLock:
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            self.insertAllChecks( args )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+
+    def randomLinkToggle( self, scheduleMethod, args=[ 5 ], blocking=True ):
+        """
+        The function randomly adds a link down-up event pair into the scheduler
+        After each individual link event, all checks are inserted into the scheduler
+        param:
+            args[0] is the average interval between link down and link up events
+            blocking means blocking other events from being scheduled between link down and link up
+        """
+        import random
+        import time
+
+        if len( args ) < 1:
+            main.log.warn( "Event Generator - Not enough arguments for randomLinkToggle: %s" % ( args ) )
+        elif len( args ) > 1:
+            main.log.warn( "Event Generator - Too many arguments for randomLinkToggle: %s" % ( args ) )
+        else:
+            downUpAvgInterval = int( args[ 0 ] )
+        with main.variableLock:
+            graphHelper = GraphHelper()
+            availableLinks = graphHelper.getNonCutEdges()
+            if len( availableLinks ) == 0:
+                main.log.warn( "All links are cut edges, aborting event" )
+                return
+            link = random.sample( availableLinks, 1 )
+
+        self.eventGeneratorLock.acquire()
+        main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
+                                           scheduleMethod,
+                                           [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
+        with main.variableLock:
+            link[ 0 ].setPendingDown()
+            link[ 0 ].backwardLink.setPendingDown()
+        sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
+        main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+        self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+        if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+            # Insert a NULL BLOCK event
+            main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+        downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
+        if not blocking:
+            self.eventGeneratorLock.release()
+            time.sleep( downUpInterval )
+            self.eventGeneratorLock.acquire()
+        else:
+            time.sleep( downUpInterval )
+        main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
+                                           scheduleMethod,
+                                           [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
+        main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+        self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+        if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+            main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+        self.eventGeneratorLock.release()
+
+    def randomLinkGroupToggle( self, scheduleMethod, args=None, blocking=True ):
+        """
+        The function randomly adds a group of link down-up events into the scheduler
+        After each link down or up, all checks are inserted into the scheduler
+        param:
+            args[0] is the number of links that are to be brought down
+            args[1] is the average interval between link down events
+            args[2] is the average interval between link group down and group up events
+            blocking means blocking other events from being scheduled between link events
+        """
+        import random
+        import time
+
+        if len( args ) < 3:
+            main.log.warn( "Event Generator - Not enough arguments for randomLinkGroupToggle: %s" % ( args ) )
+        elif len( args ) > 3:
+            main.log.warn( "Event Generator - Too many arguments for randomLinkGroupToggle: %s" % ( args ) )
+        else:
+            linkGroupSize = int( args[ 0 ] )
+            downDownAvgInterval = int( args[ 1 ] )
+            downUpAvgInterval = int( args[ 2 ] )
+        downLinks = []
+        for i in range( 0, linkGroupSize ):
+            with main.variableLock:
+                graphHelper = GraphHelper()
+                availableLinks = graphHelper.getNonCutEdges()
+                if len( availableLinks ) == 0:
+                    main.log.warn( "All links are cut edges, aborting event" )
+                    continue
+                link = random.sample( availableLinks, 1 )
+            if i == 0:
+                self.eventGeneratorLock.acquire()
+            main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
+                                               scheduleMethod,
+                                               [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
+            with main.variableLock:
+                link[ 0 ].setPendingDown()
+                link[ 0 ].backwardLink.setPendingDown()
+            downLinks.append( link[ 0 ] )
+            sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                # Insert a NULL BLOCK event
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            downDownInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
+            if not blocking:
+                self.eventGeneratorLock.release()
+                time.sleep( downDownInterval )
+                self.eventGeneratorLock.acquire()
+            else:
+                time.sleep( downDownInterval )
+
+        downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
+        if not blocking:
+            self.eventGeneratorLock.release()
+            time.sleep( downUpInterval )
+            self.eventGeneratorLock.acquire()
+        else:
+            time.sleep( downUpInterval )
+
+        for link in downLinks:
+            main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
+                                               scheduleMethod,
+                                               [ link.deviceA.name, link.deviceB.name ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            upUpInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
+            if not blocking:
+                self.eventGeneratorLock.release()
+                time.sleep( upUpInterval )
+                self.eventGeneratorLock.acquire()
+            else:
+                time.sleep( upUpInterval )
+        self.eventGeneratorLock.release()
+
+    def randomDeviceToggle( self, scheduleMethod, args=[ 5 ], blocking=True ):
+        """
+        The function randomly removes a device and then adds it back
+        After each individual device event, all checks are inserted into the scheduler
+        param:
+            args[0] is the average interval between device down and device up events
+            blocking means blocking other events from being scheduled between device down and device up
+        """
+        import random
+        import time
+
+        if len( args ) < 1:
+            main.log.warn( "Event Generator - Not enough arguments for randomDeviceToggle: %s" % ( args ) )
+        elif len( args ) > 1:
+            main.log.warn( "Event Generator - Too many arguments for randomDeviceToggle: %s" % ( args ) )
+        else:
+            downUpAvgInterval = int( args[ 0 ] )
+        with main.variableLock:
+            graphHelper = GraphHelper()
+            availableDevices = graphHelper.getNonCutVertices()
+            if len( availableDevices ) == 0:
+                main.log.warn( "All devices are Cut vertices, aborting event" )
+                return
+            device = random.sample( availableDevices, 1 )
+
+        self.eventGeneratorLock.acquire()
+        main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
+                                           scheduleMethod,
+                                           [ device[ 0 ].name ] )
+        with main.variableLock:
+            device[ 0 ].setPendingDown()
+        sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
+        main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+        self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+        if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+            # Insert a NULL BLOCK event
+            main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+        downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
+        if not blocking:
+            self.eventGeneratorLock.release()
+            time.sleep( downUpInterval )
+            self.eventGeneratorLock.acquire()
+        else:
+            time.sleep( downUpInterval )
+        main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
+                                           scheduleMethod,
+                                           [ device[ 0 ].name ] )
+        main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+        self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+        if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+            main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+        self.eventGeneratorLock.release()
+
+    def randomDeviceGroupToggle( self, scheduleMethod, args=None, blocking=True ):
+        """
+        The function randomly adds a group of device down-up events into the scheduler
+        After each device down or up, all checks are inserted into the scheduler
+        param:
+            args[0] is the number of devices that are to be brought down
+            args[1] is the average interval between device down events
+            args[2] is the average interval between device group down and group up events
+            blocking means blocking other events from being scheduled between device events
+        """
+        import random
+        import time
+
+        if len( args ) < 3:
+            main.log.warn( "Event Generator - Not enough arguments for randomDeviceGroupToggle: %s" % ( args ) )
+        elif len( args ) > 3:
+            main.log.warn( "Event Generator - Too many arguments for randomDeviceGroupToggle: %s" % ( args ) )
+        else:
+            deviceGroupSize = int( args[ 0 ] )
+            downDownAvgInterval = int( args[ 1 ] )
+            downUpAvgInterval = int( args[ 2 ] )
+        downDevices = []
+        for i in range( 0, deviceGroupSize ):
+            with main.variableLock:
+                graphHelper = GraphHelper()
+                availableDevices = graphHelper.getNonCutVertices()
+                if len( availableDevices ) == 0:
+                    main.log.warn( "All devices are cut vertices, aborting event" )
+                    continue
+                device = random.sample( availableDevices, 1 )
+            if i == 0:
+                self.eventGeneratorLock.acquire()
+            main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
+                                               scheduleMethod,
+                                               [ device[ 0 ].name ] )
+            with main.variableLock:
+                device[ 0 ].setPendingDown()
+            downDevices.append( device[ 0 ] )
+            sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                # Insert a NULL BLOCK event
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            downDownInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
+            if not blocking:
+                self.eventGeneratorLock.release()
+                time.sleep( downDownInterval )
+                self.eventGeneratorLock.acquire()
+            else:
+                time.sleep( downDownInterval )
+
+        downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
+        if not blocking:
+            self.eventGeneratorLock.release()
+            time.sleep( downUpInterval )
+            self.eventGeneratorLock.acquire()
+        else:
+            time.sleep( downUpInterval )
+
+        for device in downDevices:
+            main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
+                                               scheduleMethod,
+                                               [ device.name ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            upUpInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
+            if not blocking:
+                self.eventGeneratorLock.release()
+                time.sleep( upUpInterval )
+                self.eventGeneratorLock.acquire()
+            else:
+                time.sleep( upUpInterval )
+        self.eventGeneratorLock.release()
+
+    def installAllHostIntents( self, scheduleMethod, args=None ):
+        """
+        This function installs host intents for all host pairs
+        After all intent events are inserted, this funciton also insert intent and traffic checks
+        """
+        import itertools
+
+        with self.eventGeneratorLock:
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            availableControllers = []
+            for controller in main.controllers:
+                if controller.isUp():
+                    availableControllers.append( controller.index )
+            if len( availableControllers ) == 0:
+                main.log.warn( "Event Generator - No available controllers" )
+                return
+            hostCombos = list( itertools.combinations( main.hosts, 2 ) )
+            for i in xrange( 0, len( hostCombos ), len( availableControllers ) ):
+                for CLIIndex in availableControllers:
+                    if i >= len( hostCombos ):
+                        break
+                    main.eventScheduler.scheduleEvent( EventType().APP_INTENT_HOST_ADD,
+                                                       EventScheduleMethod().RUN_NON_BLOCK,
+                                                       [ hostCombos[ i ][ 0 ].name, hostCombos[ i ][ 1 ].name, CLIIndex ] )
+                    i += 1
+            # Pending checks after installing all intents
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            sleepTime = int( main.params[ 'EVENT' ][ 'installAllHostIntents' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+
+    def removeAllHostIntents( self, scheduleMethod, args=None ):
+        """
+        This function removes host intents for all host pairs
+        After all intent events are inserted, this funciton also insert intent and traffic checks
+        """
+        import itertools
+
+        with self.eventGeneratorLock:
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            availableControllers = []
+            for controller in main.controllers:
+                if controller.isUp():
+                    availableControllers.append( controller.index )
+            if len( availableControllers ) == 0:
+                main.log.warn( "Event Generator - No available controllers" )
+                return
+            hostCombos = list( itertools.combinations( main.hosts, 2 ) )
+            for i in xrange( 0, len( hostCombos ), len( availableControllers ) ):
+                for CLIIndex in availableControllers:
+                    if i >= len( hostCombos ):
+                        break
+                    main.eventScheduler.scheduleEvent( EventType().APP_INTENT_HOST_DEL,
+                                                       EventScheduleMethod().RUN_NON_BLOCK,
+                                                       [ hostCombos[ i ][ 0 ].name, hostCombos[ i ][ 1 ].name, CLIIndex ] )
+                    i += 1
+            # Pending checks after removing all intents
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            sleepTime = int( main.params[ 'EVENT' ][ 'removeAllHostIntents' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+
+    def installAllPointIntents( self, scheduleMethod, args=None ):
+        """
+        This function installs point intents for all device pairs
+        After all intent events are inserted, this funciton also insert intent and traffic checks
+        """
+        import itertools
+
+        with self.eventGeneratorLock:
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            availableControllers = []
+            for controller in main.controllers:
+                if controller.isUp():
+                    availableControllers.append( controller.index )
+            if len( availableControllers ) == 0:
+                main.log.warn( "Event Generator - No available controllers" )
+                return
+            deviceCombos = list( itertools.permutations( main.devices, 2 ) )
+            for i in xrange( 0, len( deviceCombos ), len( availableControllers ) ):
+                for CLIIndex in availableControllers:
+                    if i >= len( deviceCombos ):
+                        break
+                    main.eventScheduler.scheduleEvent( EventType().APP_INTENT_POINT_ADD,
+                                                       EventScheduleMethod().RUN_NON_BLOCK,
+                                                       [ deviceCombos[ i ][ 0 ].name, deviceCombos[ i ][ 1 ].name, CLIIndex ] )
+                    i += 1
+            # Pending checks after installing all intents
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            sleepTime = int( main.params[ 'EVENT' ][ 'installAllPointIntents' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+
+    def removeAllPointIntents( self, scheduleMethod, args=None ):
+        """
+        This function removes point intents for all device pairs
+        After all intent events are inserted, this funciton also insert intent and traffic checks
+        """
+        import itertools
+
+        with self.eventGeneratorLock:
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            availableControllers = []
+            for controller in main.controllers:
+                if controller.isUp():
+                    availableControllers.append( controller.index )
+            if len( availableControllers ) == 0:
+                main.log.warn( "Event Generator - No available controllers" )
+                return
+            deviceCombos = list( itertools.permutations( main.devices, 2 ) )
+            for i in xrange( 0, len( deviceCombos ), len( availableControllers ) ):
+                for CLIIndex in availableControllers:
+                    if i >= len( deviceCombos ):
+                        break
+                    main.eventScheduler.scheduleEvent( EventType().APP_INTENT_POINT_DEL,
+                                                       EventScheduleMethod().RUN_NON_BLOCK,
+                                                       [ deviceCombos[ i ][ 0 ].name, deviceCombos[ i ][ 1 ].name, CLIIndex ] )
+                    i += 1
+            # Pending checks after removing all intents
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
+            sleepTime = int( main.params[ 'EVENT' ][ 'removeAllPointIntents' ][ 'sleepBeforeCheck' ] )
+            main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py b/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py
new file mode 100644
index 0000000..7e4c95c
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py
@@ -0,0 +1,197 @@
+"""
+This file contains the event scheduler class for CHOTestMonkey
+Author: you@onlab.us
+"""
+from threading import Lock, Condition
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.events.TestEvent import *
+from tests.CHOTestMonkey.dependencies.events.CheckEvent import *
+from tests.CHOTestMonkey.dependencies.events.NetworkEvent import *
+from tests.CHOTestMonkey.dependencies.events.AppEvent import *
+from tests.CHOTestMonkey.dependencies.events.ONOSEvent import *
+
+class EventScheduleMethod:
+    def __init__( self ):
+        self.map = {}
+        self.RUN_NON_BLOCK = 1
+        self.map[ 1 ] = 'RUN_NON_BLOCK'
+        self.RUN_BLOCK = -1
+        self.map[ -1 ] = 'RUN_BLOCK'
+
+class EventTuple:
+    def __init__( self, id, className, typeString, typeIndex, scheduleMethod, args, rerunInterval, maxRerunNum ):
+        self.default = ''
+        self.id = 0
+        self.className = className
+        self.typeString = typeString
+        self.typeIndex = typeIndex
+        self.scheduleMethod = scheduleMethod
+        self.args = args
+        self.rerunInterval = rerunInterval
+        self.maxRerunNum = maxRerunNum
+
+    def startEvent( self ):
+        assert self.className in globals().keys()
+        event = globals()[ self.className ]
+        return event().startEvent( self.args )
+
+class EventScheduler:
+    def __init__( self ):
+        self.default = ''
+        self.pendingEvents = []
+        self.pendingEventsCondition = Condition()
+        self.runningEvents = []
+        self.runningEventsCondition = Condition()
+        self.isRunning = True
+        self.idleCondition = Condition()
+        self.pendingEventsCapacity = int( main.params[ 'SCHEDULER' ][ 'pendingEventsCapacity' ] )
+        self.runningEventsCapacity = int( main.params[ 'SCHEDULER' ][ 'runningEventsCapacity' ] )
+        self.scheduleLoopSleep = float( main.params[ 'SCHEDULER' ][ 'scheduleLoopSleep' ] )
+
+    def scheduleEvent( self, typeIndex, scheduleMethod, args=None, index=-1 ):
+        """
+        Insert an event to pendingEvents
+        param:
+            index: the position to insert into pendingEvents, default value -1 implies the tail of pendingEvents
+        """
+        if not typeIndex in main.enabledEvents.keys():
+            main.log.warn( "Event Scheduler - event type %s not enabled" % ( typeIndex ) )
+            return
+        if main.enabledEvents[ typeIndex ] in main.params[ 'EVENT' ].keys():
+            if 'rerunInterval' in main.params[ 'EVENT' ][ main.enabledEvents[ typeIndex ] ].keys():
+                rerunInterval = int( main.params[ 'EVENT' ][ main.enabledEvents[ typeIndex ] ][ 'rerunInterval' ] )
+                maxRerunNum = int( main.params[ 'EVENT' ][ main.enabledEvents[ typeIndex ] ][ 'maxRerunNum' ] )
+            else:
+                rerunInterval = int( main.params[ 'EVENT' ][ 'Event' ][ 'rerunInterval' ] )
+                maxRerunNum = int( main.params[ 'EVENT' ][ 'Event' ][ 'maxRerunNum' ] )
+        eventTuple = EventTuple( main.eventID, main.enabledEvents[ typeIndex ], EventType().map[ typeIndex ], typeIndex, scheduleMethod, args, rerunInterval, maxRerunNum )
+        with main.variableLock:
+            main.eventID += 1
+        main.log.debug( "Event Scheduler - Event added: %s, %s, %s" % ( typeIndex,
+                                                                       scheduleMethod,
+                                                                       args ) )
+        with self.pendingEventsCondition:
+            if index == -1:
+                self.pendingEvents.append( eventTuple )
+            elif index > -1 and index <= len( self.pendingEvents ):
+                self.pendingEvents.insert( index, eventTuple )
+            else:
+                main.log.warn( "Event Scheduler - invalid index when isnerting event: %s" % ( index ) )
+            self.pendingEventsCondition.notify()
+        self.printEvents()
+
+    def startScheduler( self ):
+        """
+        Start the loop which schedules the events in pendingEvents
+        """
+        import time
+
+        while 1:
+            with self.pendingEventsCondition:
+                while len( self.pendingEvents ) == 0:
+                    self.pendingEventsCondition.wait()
+                eventTuple = self.pendingEvents[ 0 ]
+            main.log.debug( "Event Scheduler - Scheduling event: %s, %s, %s" % ( eventTuple.typeIndex,
+                                                                                eventTuple.scheduleMethod,
+                                                                                eventTuple.args ) )
+            if eventTuple.scheduleMethod == EventScheduleMethod().RUN_NON_BLOCK:
+                # Run NON_BLOCK events using threads
+                with self.pendingEventsCondition:
+                    self.pendingEvents.remove( eventTuple )
+                t = main.Thread( target=self.startEvent,
+                                 threadID=main.threadID,
+                                 name="startEvent",
+                                 args=[ eventTuple ])
+                t.start()
+                with main.variableLock:
+                    main.threadID += 1
+            elif eventTuple.scheduleMethod == EventScheduleMethod().RUN_BLOCK:
+                # Wait for all other events before start
+                with self.runningEventsCondition:
+                    while not len( self.runningEvents ) == 0:
+                        self.runningEventsCondition.wait()
+                # BLOCK events will temporarily block the following events until finish running
+                with self.pendingEventsCondition:
+                    self.pendingEvents.remove( eventTuple )
+                self.startEvent( eventTuple )
+            else:
+                with self.pendingEventsCondition:
+                    self.pendingEvents.remove( eventTuple )
+            time.sleep( self.scheduleLoopSleep )
+
+    def startEvent( self, eventTuple ):
+        """
+        Start a network/ONOS/application event
+        """
+        import time
+
+        with self.runningEventsCondition:
+            self.runningEvents.append( eventTuple )
+        self.printEvents()
+        rerunNum = 0
+        result = eventTuple.startEvent()
+        while result == EventStates().FAIL and rerunNum < eventTuple.maxRerunNum:
+            time.sleep( eventTuple.rerunInterval )
+            rerunNum += 1
+            main.log.debug( eventTuple.typeString + ": retry number " + str( rerunNum ) )
+            result = eventTuple.startEvent()
+        if result == EventStates().FAIL:
+            main.log.error( eventTuple.typeString + " failed" )
+            main.caseResult = main.FALSE
+            if main.params[ 'TEST' ][ 'pauseTest' ] == 'on':
+                #self.isRunning = False
+                #main.log.error( "Event Scheduler - Test paused. To resume test, run \'resume-test\' command in CLI debugging mode" )
+                main.stop()
+        with self.runningEventsCondition:
+            self.runningEvents.remove( eventTuple )
+            if len( self.runningEvents ) == 0:
+                self.runningEventsCondition.notify()
+                with self.pendingEventsCondition:
+                    if len( self.pendingEvents ) == 0:
+                        with self.idleCondition:
+                            self.idleCondition.notify()
+        self.printEvents()
+
+    def printEvents( self ):
+        """
+        Print all the events in pendingEvents and runningEvents
+        """
+        events = " ["
+        with self.runningEventsCondition:
+            for index in range( 0, len( self.runningEvents ) - 1 ):
+                events += str( self.runningEvents[ index ].typeIndex )
+                events += ", "
+            if len( self.runningEvents ) > 0:
+                events += str( self.runningEvents[ -1 ].typeIndex )
+        events += "]"
+        events += " ["
+        with self.pendingEventsCondition:
+            for index in range( 0, len( self.pendingEvents ) - 1 ):
+                events += str( self.pendingEvents[ index ].typeIndex )
+                events += ", "
+            if len( self.pendingEvents ) > 0:
+                events += str( self.pendingEvents[ -1 ].typeIndex )
+        events += "]"
+        main.log.debug( "Event Scheduler - Events: " + events )
+
+    def isAvailable( self ):
+        with self.pendingEventsCondition:
+            with self.runningEventsCondition:
+                return len( self.pendingEvents ) < self.pendingEventsCapacity and\
+                       len( self.runningEvents ) < self.runningEventsCapacity and\
+                       self.isRunning
+
+    def isIdle( self ):
+        with self.pendingEventsCondition:
+            with self.runningEventsCondition:
+                return len( self.pendingEvents ) == 0 and\
+                       len( self.runningEvents ) == 0 and\
+                       self.isRunning
+
+    def setPendingEventsCapacity( self, capacity ):
+        self.pendingEventsCapacity = capacity
+
+    def setRunningState( self, state ):
+        assert state == True or state == False
+        self.isRunning = state
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py b/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py
new file mode 100644
index 0000000..0c3e0e0
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py
@@ -0,0 +1,68 @@
+"""
+Insert network/ONOS/app events into CHOTestMonkey
+Author: you@onlab.us
+"""
+import time
+import random
+from multiprocessing.connection import Client
+
+def triggerEvent( type, scheduleMethod, *args ):
+    """
+    This function inserts an event into CHOTestMonkey
+    """
+    host = "localhost"
+    port = 6000
+    address = ( host, port )
+    conn = Client( address )
+    request = []
+    request.append( 2 )
+    request.append( type )
+    request.append( scheduleMethod )
+    for arg in args:
+        request.append( arg )
+    conn.send( request )
+    response = conn.recv()
+    while response == 11:
+        time.sleep( 1 )
+        conn.send( request )
+        response = conn.recv()
+    if response == 10:
+        print "Event inserted:", type, scheduleMethod, args
+    elif response == 20:
+        print "Unknown message to server"
+    elif response == 21:
+        print "Unknown event type to server"
+    elif response == 22:
+        print "Unknown schedule method to server"
+    elif response == 23:
+        print "Not enough argument"
+    else:
+        print "Unknown response from server:", response
+    conn.close()
+
+def testLoop( sleepTime=5 ):
+    downLinkNum = 0
+    downDeviceNum = 0
+    while True:
+        r = random.random()
+        if r < 0.2:
+            triggerEvent( 'NETWORK_LINK_DOWN', 'RUN_BLOCK', 'random', 'random' )
+            downLinkNum += 1
+            time.sleep( sleepTime )
+        elif r < 0.4:
+            triggerEvent( 'NETWORK_DEVICE_DOWN', 'RUN_BLOCK', 'random' )
+            downDeviceNum += 1
+            time.sleep( sleepTime * 2 )
+        elif r < 0.7 and downLinkNum > 0:
+            triggerEvent( 'NETWORK_LINK_UP', 'RUN_BLOCK', 'random', 'random' )
+            downLinkNum -= 1
+            time.sleep( sleepTime )
+        elif downDeviceNum > 0:
+            triggerEvent( 'NETWORK_DEVICE_UP', 'RUN_BLOCK', 'random' )
+            downDeviceNum -= 1
+            time.sleep( sleepTime * 2 )
+        else:
+            pass
+
+if __name__ == '__main__':
+    testLoop( 2 )
diff --git a/TestON/tests/CHOTestMonkey/dependencies/GraphHelper.py b/TestON/tests/CHOTestMonkey/dependencies/GraphHelper.py
new file mode 100644
index 0000000..13f110b
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/GraphHelper.py
@@ -0,0 +1,133 @@
+"""
+Graph algorithm implementations for CHOTestMonkey
+Author: you@onlab.us
+"""
+class GraphHelper:
+    """
+    This class implements graph algorithms for CHOTestMonkey.
+    It reads main.devices and main.links as vertices and edges.
+    Currently it offers functions for finding (non-)cut-edges and vertices,
+    which is realized based on chain-decomposition algorithm
+    """
+    def __init__( self ):
+        # Depth-first index of each node
+        self.DFI = []
+        # Parent vertex and egde of each node in depth-first search tree
+        self.parentDeviceInDFS = []
+        self.parentLinkInDFS = []
+        # Data structures for chain-decomposition algorithm
+        self.backEdges = {}
+        self.chains = []
+        self.currentDFI = 0
+        self.upDevices = []
+        for device in main.devices:
+            if device.isUp():
+                self.upDevices.append( device )
+        for i in range( len( main.devices ) ):
+            self.DFI.append( -1 )
+            self.parentDeviceInDFS.append( None )
+            self.parentLinkInDFS.append( None )
+
+    def genDFIandBackEdge( self, device ):
+        """
+        This function runs a depth-first search and get DFI of each node
+        as well as collect the back edges
+        """
+        self.DFI[ device.index ] = self.currentDFI
+        self.currentDFI += 1
+        for link in device.outgoingLinks:
+            if not link.isUp():
+                continue
+            backwardLink = link.backwardLink
+            neighbor = link.deviceB
+            if neighbor == self.parentDeviceInDFS[ device.index ]:
+                continue
+            elif self.DFI[ neighbor.index ] == -1:
+                self.parentDeviceInDFS[ neighbor.index ] = device
+                self.parentLinkInDFS[ neighbor.index ] = backwardLink
+                self.genDFIandBackEdge( neighbor )
+            else:
+                key = self.DFI[ neighbor.index ]
+                if key in self.backEdges.keys():
+                    if not link in self.backEdges[ key ] and\
+                    not backwardLink in self.backEdges[ key ]:
+                        self.backEdges[ key ].append( backwardLink )
+                else:
+                    tempKey = self.DFI[ device.index ]
+                    if tempKey in self.backEdges.keys():
+                        if not link in self.backEdges[ tempKey ] and\
+                        not backwardLink in self.backEdges[ tempKey ]:
+                            self.backEdges[ key ] = [ backwardLink ]
+                    else:
+                        self.backEdges[ key ] = [ backwardLink ]
+
+    def findChains( self ):
+        """
+        This function finds all the 'chains' for chain-decomposition algorithm
+        """
+        keyList = self.backEdges.keys()
+        keyList.sort()
+        deviceIsVisited = []
+        for i in range( len( main.devices ) ):
+            deviceIsVisited.append( 0 )
+        for key in keyList:
+            backEdgeList = self.backEdges[ key ]
+            for link in backEdgeList:
+                chain = []
+                currentLink = link
+                sourceDevice = link.deviceA
+                while True:
+                    currentDevice = currentLink.deviceA
+                    nextDevice = currentLink.deviceB
+                    deviceIsVisited[ currentDevice.index ] = 1
+                    chain.append( currentLink )
+                    if nextDevice == sourceDevice or deviceIsVisited[ nextDevice.index ] == 1:
+                        break
+                    currentLink = self.parentLinkInDFS[ nextDevice.index ]
+                self.chains.append( chain )
+
+    def getNonCutEdges( self ):
+        """
+        This function returns all non-cut-edges of a graph
+        """
+        assert len( self.upDevices ) != 0
+        self.genDFIandBackEdge( self.upDevices[ 0 ] )
+        self.findChains()
+        nonCutEdges = []
+        for chain in self.chains:
+            for link in chain:
+                nonCutEdges.append( link )
+        return nonCutEdges
+
+    def getNonCutVertices( self ):
+        """
+        This function returns all non-cut-vertices of a graph
+        """
+        nonCutEdges = self.getNonCutEdges()
+        nonCutVertices = []
+        for device in self.upDevices:
+            deviceIsNonCut = True
+            for link in device.outgoingLinks:
+                if link.isUp() and not ( link in nonCutEdges or link.backwardLink in nonCutEdges ):
+                    deviceIsNonCut = False
+                    break
+            if deviceIsNonCut:
+                nonCutVertices.append( device )
+        return nonCutVertices
+
+    def printDFI( self ):
+        print self.DFI
+
+    def printParentInDFS( self ):
+        print self.parentInDFS
+
+    def printBackEdges( self ):
+        print self.backEdges
+
+    def printChains( self ):
+        chainIndex = 0
+        for chain in self.chains:
+            print chainIndex
+            for link in chain:
+                print link
+            chainIndex += 1
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/CHOTestMonkey/dependencies/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/CHOTestMonkey/dependencies/__init__.py
diff --git a/TestON/tests/CHOTestMonkey/dependencies/cli.py b/TestON/tests/CHOTestMonkey/dependencies/cli.py
new file mode 100644
index 0000000..d1a8448
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/cli.py
@@ -0,0 +1,109 @@
+"""
+Start CLI for CHOTestMonkey
+Author: you@onlab.us
+"""
+from multiprocessing.connection import Client
+
+commandMap = {}
+paramNum = {}
+
+def triggerEvent( debugMode, name, scheduleMethod, args ):
+    """
+    This function inserts an event from CLI to CHOTestMonkey
+    """
+    host = "localhost"
+    port = 6000
+    address = ( host, port )
+    conn = Client( address )
+    request = []
+    if debugMode:
+        request.append( 2 )
+    else:
+        request.append( 1 )
+    request.append( name )
+    request.append( scheduleMethod )
+    for arg in args:
+        request.append( arg )
+    conn.send( request )
+    response = conn.recv()
+    return response
+
+def startCLI():
+    debugMode = False
+    while 1:
+        try:
+            if debugMode:
+                cmd = raw_input("CHOTestMonkey-debug>")
+            else:
+                cmd = raw_input("CHOTestMonkey>")
+        except EOFError:
+            print "exit"
+            return
+        except Exception:
+            print "Uncaught exception!"
+            return
+
+        if cmd == 'help':
+            print 'Supported commands:'
+            print 'help'
+            print 'debug'
+            print 'exit'
+            for command in commandMap.keys():
+                print command
+        elif cmd == '':
+            pass
+        elif cmd == 'debug':
+            debugMode = True
+        elif cmd == 'exit':
+            if debugMode:
+                debugMode = False
+            else:
+                return
+        else:
+            cmdList = cmd.split( ' ' )
+            if cmdList[ 0 ] in commandMap.keys():
+                num = paramNum[ cmdList[ 0 ] ]
+                name = commandMap[ cmdList[ 0 ] ]
+                if len( cmdList ) < num + 1:
+                    print 'not enough arguments'
+                elif len( cmdList ) > num + 1:
+                    print 'Too many arguments'
+                else:
+                    result = triggerEvent( debugMode, name, 'RUN_BLOCK', cmdList[ 1: ] )
+                    if result == 10:
+                        pass
+                    elif result == 11:
+                        print "Scheduler busy...Try later or use debugging mode by entering \'debug\'"
+                    elif result == 20:
+                        print "Unknown message to server"
+                    elif result == 21:
+                        print "Unknown event type to server"
+                    elif result == 22:
+                        print "Unknown schedule method to server"
+                    elif result == 23:
+                        print "Not enough argument"
+                    else:
+                        print "Unknown response from server"
+            else:
+                print 'Unknown command'
+
+if __name__ == '__main__':
+    import xml.etree.ElementTree
+    try:
+        root = xml.etree.ElementTree.parse( '../CHOTestMonkey.params' ).getroot()
+    except Exception:
+        print "Uncaught exception!"
+    for child in root:
+        if child.tag == 'EVENT':
+            for event in child:
+                for item in event:
+                    if item.tag == 'CLI':
+                        CLI = str( item.text )
+                    if item.tag == 'typeString':
+                        name = str( item.text )
+                    if item.tag == 'CLIParamNum':
+                        num = int( item.text )
+                commandMap[ CLI ] = name
+                paramNum[ CLI ] = num
+    startCLI()
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py b/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py
new file mode 100644
index 0000000..2a18ac7
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py
@@ -0,0 +1,80 @@
+"""
+This file contains device, host and link class for CHOTestMonkey
+Author: you@onlab.us
+"""
+
+class NetworkElement:
+    def __init__( self, index ):
+        self.default = ''
+        self.index = index
+        self.status = 'up'
+
+    def isUp( self ):
+        return self.status == 'up'
+
+    def isDown( self ):
+        return self.status == 'down'
+
+    def isRemoved( self ):
+        return self.status == 'removed'
+
+    def setPendingDown( self ):
+        self.status = 'pending_down'
+
+    def setRemoved( self ):
+        self.status = 'removed'
+
+    def bringDown( self ):
+        self.status = 'down'
+
+    def bringUp( self ):
+        self.status = 'up'
+
+class Device( NetworkElement ):
+    def __init__( self, index, name, dpid ):
+        NetworkElement.__init__( self, index )
+        self.name = name
+        self.dpid = dpid
+        self.hosts = []
+        # For each bidirectional link, we only store one direction here
+        self.outgoingLinks = []
+
+    def __str__( self ):
+        return "name: " + self.name + ", dpid: " + self.dpid
+
+class Host( NetworkElement ):
+    def __init__( self, index, name, id, mac, device, devicePort, vlan, ipAddresses ):
+        NetworkElement.__init__( self, index )
+        self.name = name
+        self.id = id
+        self.mac = mac
+        self.device = device
+        self.devicePort = devicePort
+        self.vlan = vlan
+        self.ipAddresses = ipAddresses
+        self.correspondents = []
+        self.handle = None
+
+    def __str__( self ):
+        return "name: " + self.name + ", mac: " + self.mac + ", device: " + self.device.dpid + ", ipAddresses: " + str( self.ipAddresses )
+
+    def setHandle( self, handle ):
+        self.handle = handle
+
+class Link( NetworkElement ):
+    """
+    Unidirectional link
+    """
+    def __init__( self, index, deviceA, portA, deviceB, portB ):
+        NetworkElement.__init__( self, index )
+        self.backwardLink = None
+        self.deviceA = deviceA
+        self.portA = portA
+        self.deviceB = deviceB
+        self.portB = portB
+
+    def __str__( self ):
+        return self.deviceA.dpid + "/" + self.portA + " - " + self.deviceB.dpid + "/" + self.portB
+
+    def setBackwardLink( self, link ):
+        self.backwardLink = link
diff --git a/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py b/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py
new file mode 100644
index 0000000..6831811
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py
@@ -0,0 +1,76 @@
+"""
+This file contains intent class for CHOTestMonkey
+Author: you@onlab.us
+"""
+from threading import Lock
+
+class Controller:
+    def __init__( self, index ):
+        self.default = ''
+        self.index = index
+        self.ip = main.onosIPs[ index - 1 ]
+        self.CLI = None
+        self.CLILock = Lock()
+        self.status = 'up'
+
+    def setCLI( self, CLI ):
+        self.CLI = CLI
+
+    def startCLI( self ):
+        return self.CLI.startOnosCli( self.ip )
+
+    def isUp( self ):
+        return self.status == 'up'
+
+    def bringDown( self ):
+        self.status = 'down'
+
+    def bringUp( self ):
+        self.status = 'up'
+
+class Intent:
+    def __init__( self, id ):
+        self.default = ''
+        self.type = 'INTENT'
+        self.id = id
+        self.expectedState = 'INSTALLED'
+
+    def isHostIntent( self ):
+        return self.type == 'INTENT_HOST'
+
+    def isPointIntent( self ):
+        return self.type == 'INTENT_POINT'
+
+    def isFailed( self ):
+        return self.expectedState == 'FAILED'
+
+    def isInstalled( self ):
+        return self.expectedState == 'INSTALLED'
+
+    def setFailed( self ):
+        self.expectedState = 'FAILED'
+
+    def setInstalled( self ):
+        self.expectedState = 'INSTALLED'
+
+class HostIntent( Intent ):
+    def __init__( self, id, hostA, hostB ):
+        Intent.__init__( self, id )
+        self.type = 'INTENT_HOST'
+        self.hostA = hostA
+        self.hostB = hostB
+        self.deviceA = hostA.device
+        self.deviceB = hostB.device
+
+    def __str__( self ):
+        return "ID: " + self.id
+
+class PointIntent( Intent ):
+    def __init__( self, id, deviceA, deviceB ):
+        Intent.__init__( self, id )
+        self.type = 'INTENT_POINT'
+        self.deviceA = deviceA
+        self.deviceB = deviceB
+
+    def __str__( self ):
+        return "ID: " + self.id
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/CHOTestMonkey/dependencies/elements/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/CHOTestMonkey/dependencies/elements/__init__.py
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
new file mode 100644
index 0000000..7f0ca7c
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
@@ -0,0 +1,246 @@
+"""
+This file contains classes for CHOTestMonkey that are related to application event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.elements.ONOSElement import HostIntent, PointIntent
+
+class IntentEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        # The index of the ONOS CLI that is going to run the command
+        self.CLIIndex = 0
+
+class HostIntentEvent( IntentEvent ):
+    def __init__( self ):
+        IntentEvent.__init__( self )
+        self.hostA = None
+        self.hostB = None
+
+    def startHostIntentEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if self.typeIndex == EventType().APP_INTENT_HOST_ADD or self.typeIndex == EventType().APP_INTENT_HOST_DEL:
+                if len( args ) < 3:
+                    main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                elif len( args ) > 3:
+                    main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                else:
+                    if args[ 0 ] == args[ 1 ]:
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    for host in main.hosts:
+                        if host.name == args[ 0 ]:
+                            self.hostA = host
+                        elif host.name == args[ 1 ]:
+                            self.hostB = host
+                        if self.hostA != None and self.hostB != None:
+                            break
+                    if self.hostA == None:
+                        main.log.warn( "Host %s does not exist: " % ( args[ 0 ] ) )
+                        return EventStates().ABORT
+                    if self.hostB == None:
+                        main.log.warn( "Host %s does not exist: " % ( args[ 1 ] ) )
+                        return EventStates().ABORT
+                    index = int( args[ 2 ] )
+                    if index < 1 or index > int( main.numCtrls ):
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    if not main.controllers[ index - 1 ].isUp():
+                        main.log.warn( self.typeString + " - invalid argument: onos %s is down" % ( controller.index ) )
+                        return EventStates().ABORT
+                    self.CLIIndex = index
+                    return self.startHostIntentEvent()
+
+class AddHostIntent( HostIntentEvent ):
+    """
+    Add a host-to-host intent (bidirectional)
+    """
+    def __init__( self ):
+        HostIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startHostIntentEvent( self ):
+        assert self.hostA != None and self.hostB != None
+        # Check whether there already exists some intent for the host pair
+        # For now we should avoid installing overlapping intents
+        for intent in main.intents:
+            if not intent.type == 'INTENT_HOST':
+                continue
+            if intent.hostA == self.hostA and intent.hostB == self.hostB or\
+            intent.hostB == self.hostA and intent.hostA == self.hostB:
+                main.log.warn( self.typeString + " - find an exiting intent for the host pair, abort installation" )
+                return EventStates().ABORT
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            id = controller.CLI.addHostIntent( self.hostA.id, self.hostB.id )
+        if id == None:
+            main.log.warn( self.typeString + " - add host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            newHostIntent = HostIntent( id, self.hostA, self.hostB )
+            main.intents.append( newHostIntent )
+            # Update host connectivity status
+            # TODO: should we check whether hostA and hostB are already correspondents?
+            self.hostB.correspondents.append( self.hostA )
+            self.hostA.correspondents.append( self.hostB )
+        return EventStates().PASS
+
+class DelHostIntent( HostIntentEvent ):
+    """
+    Delete a host-to-host intent (bidirectional)
+    """
+    def __init__( self ):
+        HostIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startHostIntentEvent( self ):
+        assert self.hostA != None and self.hostB != None
+        targetIntent = None
+        for intent in main.intents:
+            if not intent.type == 'INTENT_HOST':
+                continue
+            if intent.hostA == self.hostA and intent.hostB == self.hostB or\
+            intent.hostB == self.hostA and intent.hostA == self.hostB:
+                targetIntent = intent
+                break
+        if targetIntent == None:
+            main.log.warn( self.typeString + " - intent does not exist" )
+            return EventStates().FAIL
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.removeIntent( targetIntent.id, purge=True )
+        if result == None or result == main.FALSE:
+            main.log.warn( self.typeString + " - delete host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            main.intents.remove( targetIntent )
+            # Update host connectivity status
+            self.hostB.correspondents.remove( self.hostA )
+            self.hostA.correspondents.remove( self.hostB )
+        return EventStates().PASS
+
+class PointIntentEvent( IntentEvent ):
+    def __init__( self ):
+        IntentEvent.__init__( self )
+        self.deviceA = None
+        self.deviceB = None
+
+    def startPointIntentEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if self.typeIndex == EventType().APP_INTENT_POINT_ADD or self.typeIndex == EventType().APP_INTENT_POINT_DEL:
+                if len( args ) < 3:
+                    main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                elif len( args ) > 3:
+                    main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                else:
+                    for device in main.devices:
+                        if device.name == args[ 0 ]:
+                            self.deviceA = device
+                        elif device.name == args[ 1 ]:
+                            self.deviceB = device
+                        if self.deviceA != None and self.deviceB != None:
+                            break
+                    if self.deviceA == None:
+                        main.log.warn( "Device %s does not exist: " % ( args[ 0 ] ) )
+                        return EventStates().ABORT
+                    if self.deviceB == None:
+                        main.log.warn( "Device %s does not exist: " % ( args[ 1 ] ) )
+                        return EventStates().ABORT
+                    index = int( args[ 2 ] )
+                    if index < 1 or index > int( main.numCtrls ):
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    if not main.controllers[ index - 1 ].isUp():
+                        main.log.warn( self.typeString + " - invalid argument: onos %s is down" % ( controller.index ) )
+                        return EventStates().ABORT
+                    self.CLIIndex = index
+                    return self.startPointIntentEvent()
+
+class AddPointIntent( PointIntentEvent ):
+    """
+    Add a point-to-point intent
+    """
+    def __init__( self ):
+        PointIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startPointIntentEvent( self ):
+        assert self.deviceA != None and self.deviceB != None
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        # TODO: the following check only work when we use default port number for point intents
+        # Check whether there already exists some intent for the device pair
+        # For now we should avoid installing overlapping intents
+        for intent in main.intents:
+            if not intent.type == 'INTENT_POINT':
+                continue
+            if intent.deviceA == self.deviceA and intent.deviceB == self.deviceB:
+                main.log.warn( self.typeString + " - find an exiting intent for the device pair, abort installation" )
+                return EventStates().ABORT
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            # TODO: handle the case that multiple hosts attach to one device
+            id = controller.CLI.addPointIntent( self.deviceA.dpid, self.deviceB.dpid,
+                                                1, 1, '',
+                                                self.deviceA.hosts[ 0 ].mac,
+                                                self.deviceB.hosts[ 0 ].mac )
+        if id == None:
+            main.log.warn( self.typeString + " - add point intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            newPointIntent = PointIntent( id, self.deviceA, self.deviceB )
+            main.intents.append( newPointIntent )
+            # Update host connectivity status
+            for hostA in self.deviceA.hosts:
+                for hostB in self.deviceB.hosts:
+                    hostA.correspondents.append( hostB )
+        return EventStates().PASS
+
+class DelPointIntent( PointIntentEvent ):
+    """
+    Delete a point-to-point intent
+    """
+    def __init__( self ):
+        PointIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startPointIntentEvent( self ):
+        assert self.deviceA != None and self.deviceB != None
+        targetIntent = None
+        for intent in main.intents:
+            if not intent.type == 'INTENT_POINT':
+                continue
+            if intent.deviceA == self.deviceA and intent.deviceB == self.deviceB:
+                targetIntent = intent
+                break
+        if targetIntent == None:
+            main.log.warn( self.typeString + " - intent does not exist" )
+            return EventStates().FAIL
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.removeIntent( targetIntent.id, purge=True )
+        if result == None or result == main.FALSE:
+            main.log.warn( self.typeString + " - delete host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            main.intents.remove( targetIntent )
+            # Update host connectivity status
+            for hostA in self.deviceA.hosts:
+                for hostB in self.deviceB.hosts:
+                    hostA.correspondents.remove( hostB )
+        return EventStates().PASS
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
new file mode 100644
index 0000000..d0408f9
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -0,0 +1,273 @@
+"""
+This file contains classes for CHOTestMonkey that are related to check event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+
+class CheckEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+
+    def startCheckEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            result = self.startCheckEvent()
+            return result
+
+class IntentCheck( CheckEvent ):
+    def __init__( self ):
+        CheckEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCheckEvent( self, args=None ):
+        checkResult = EventStates().PASS
+        intentDict = {}
+        for intent in main.intents:
+            intentDict[ intent.id ] = intent.expectedState
+        for controller in main.controllers:
+            if controller.isUp():
+                with controller.CLILock:
+                    intentState = controller.CLI.compareIntent( intentDict )
+                if not intentState:
+                    main.log.warn( "Intent Check - not all intent ids and states match that on ONOS%s" % ( controller.index ) )
+                    checkResult = EventStates().FAIL
+        return checkResult
+
+class FlowCheck( CheckEvent ):
+    def __init__( self ):
+        CheckEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCheckEvent( self, args=None ):
+        import json
+        checkResult = EventStates().PASS
+        for controller in main.controllers:
+            if controller.isUp():
+                with controller.CLILock:
+                    flows = controller.CLI.flows()
+                    try:
+                        flows = json.loads( flows )
+                    except ( TypeError, ValueError ):
+                        main.log.exception( "Flow Check - Object not as expected: {!r}".format( flows ) )
+                        return EventStates().FAIL
+                    # Compare flow IDs in ONOS and Mininet
+                    flowIDList = []
+                    for item in flows:
+                        for flow in item[ "flows" ]:
+                            flowIDList.append( hex( int( flow[ 'id' ] ) ) )
+                    main.log.info( "Flow Check - current flow number on ONOS%s: %s" % ( controller.index, len( flowIDList ) ) )
+                    switchList = []
+                    for device in main.devices:
+                        switchList.append( device.name )
+                    with main.mininetLock:
+                        flowCompareResult = main.Mininet1.checkFlowId( switchList, flowIDList, debug=False )
+                    if not flowCompareResult:
+                        main.log.warn( "Flow Check - flows on ONOS%s do not match that in Mininet" % ( controller.index ) )
+                        checkResult = EventStates().FAIL
+                    # Check flow state
+                    flowState = controller.CLI.checkFlowsState( isPENDING=False )
+                    if not flowState:
+                        main.log.warn( "Flow Check - not all flows are in ADDED state on ONOS%s" % ( controller.index ) )
+                        checkResult = EventStates().FAIL
+        return checkResult
+
+class TopoCheck( CheckEvent ):
+    def __init__( self ):
+        CheckEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCheckEvent( self, args=None ):
+        import json
+        checkResult = EventStates().PASS
+        upLinkNum = 0
+        upDeviceNum = 0
+        upHostNum = 0
+        with main.variableLock:
+            for link in main.links:
+                if not link.isDown() and not link.isRemoved():
+                    upLinkNum += 1
+            for device in main.devices:
+                if not device.isDown() and not device.isRemoved():
+                    upDeviceNum += 1
+            for host in main.hosts:
+                if not host.isDown() and not host.isRemoved():
+                    upHostNum += 1
+        clusterNum = 1
+        for controller in main.controllers:
+            if controller.isUp():
+                with controller.CLILock:
+                    topologyOutput = controller.CLI.topology()
+                    topoState = controller.CLI.checkStatus( topologyOutput, upDeviceNum, upLinkNum )
+                    #if not topoState:
+                    #    main.log.warn( "Topo Check - link or device number discoverd by ONOS%s is incorrect" % ( controller.index ) )
+                    #    checkResult = EventStates().FAIL
+                    # Check links
+                    try:
+                        links = controller.CLI.links()
+                        links = json.loads( links )
+                        if not len( links ) == upLinkNum:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "Topo Check - link number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upLinkNum, len( links ) ) )
+                        # Check devices
+                        devices = controller.CLI.devices()
+                        devices = json.loads( devices )
+                        availableDeviceNum = 0
+                        for device in devices:
+                            if device[ 'available' ] == True:
+                                availableDeviceNum += 1
+                        if not availableDeviceNum == upDeviceNum:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "Topo Check - device number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upDeviceNum, availableDeviceNum ) )
+                        # Check hosts
+                        hosts = controller.CLI.hosts()
+                        hosts = json.loads( hosts )
+                        if not len( hosts ) == upHostNum:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "Topo Check - host number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upHostNum, len( hosts ) ) )
+                        # Check clusters
+                        clusters = controller.CLI.clusters()
+                        clusters = json.loads( clusters )
+                        if not len( clusters ) == clusterNum:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "Topo Check - cluster number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, clusterNum, len( clusters ) ) )
+                    except ( TypeError, ValueError ):
+                        main.log.exception( "Flow Check - Object not as expected" )
+                        return EventStates().FAIL
+        return checkResult
+
+class ONOSCheck( CheckEvent ):
+    def __init__( self ):
+        CheckEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCheckEvent( self, args=None ):
+        import json
+        checkResult = EventStates().PASS
+        topics = []
+        # TODO: Other topics?
+        for i in range( 14 ):
+            topics.append( "intent-partition-" + str( i ) )
+        dpidToAvailability = {}
+        dpidToMaster = {}
+        for device in main.devices:
+            if device.isDown() or device.isRemoved():
+                dpidToAvailability[ device.dpid ] = False
+            else:
+                dpidToAvailability[ device.dpid ] = True
+            dpidToMaster[ device.dpid ] = 'unknown'
+        # Check mastership, leaders and node states on each controller node
+        for controller in main.controllers:
+            if controller.isUp():
+                # Check mastership
+                try:
+                    with controller.CLILock:
+                        roles = controller.CLI.roles()
+                    roles = json.loads( roles )
+                    for device in roles:
+                        dpid = device[ 'id' ]
+                        if dpidToMaster[ dpid ] == 'unknown':
+                            dpidToMaster[ dpid ] = device[ 'master' ]
+                        elif dpidToMaster[ dpid ] != device[ 'master' ]:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "ONOS Check - Mastership of %s on ONOS%s is inconsistent with that on ONOS1" % ( dpid, controller.index ) )
+                        if dpidToAvailability[ dpid ] and device[ 'master' ] == "none":
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "ONOS Check - Device %s has no master on ONOS%s" % ( dpid, controller.index ) )
+                    # Check leaders
+                    with controller.CLILock:
+                        leaders = controller.CLI.leaders()
+                    leaders = json.loads( leaders )
+                    ONOSTopics = [ j['topic'] for j in leaders ]
+                    for topic in topics:
+                        if topic not in ONOSTopics:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "ONOS Check - Topic %s not in leaders on ONOS%s" % ( topic, controller.index ) )
+                    # Check node state
+                    with controller.CLILock:
+                        nodes = controller.CLI.nodes()
+                    nodes = json.loads( nodes )
+                    ipToState = {}
+                    for node in nodes:
+                        ipToState[ node[ 'ip' ] ] = node[ 'state' ]
+                    for c in main.controllers:
+                        if c.isUp() and ipToState[ c.ip ] == 'READY':
+                            pass
+                        elif not c.isUp() and ipToState[ c.ip ] == 'INACTIVE':
+                            pass
+                        else:
+                            checkResult = EventStates().FAIL
+                            main.log.warn( "ONOS Check - ONOS%s shows wrong node state: ONOS%s is %s but state is %s" % ( controller.index, c.index, c.status, ipToState[ c.ip ] ) )
+                    # TODO: check partitions?
+                except ( TypeError, ValueError ):
+                    main.log.exception( "ONOS Check - Object not as expected" )
+                    return EventStates().FAIL
+        return checkResult
+
+class TrafficCheck( CheckEvent ):
+    def __init__( self ):
+        CheckEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCheckEvent( self, args=None ):
+        checkResult = EventStates().PASS
+        pool = []
+        wait = int( main.params[ 'EVENT' ][ 'TrafficCheck' ][ 'pingWait' ] )
+        timeout = int( main.params[ 'EVENT' ][ 'TrafficCheck' ][ 'pingTimeout' ] )
+        dstIPv4List = {}
+        dstIPv6List = {}
+        upHosts = []
+        for host in main.hosts:
+            if host.isUp():
+                upHosts.append( host )
+        for host in upHosts:
+            dstIPv4List[ host.index ] = []
+            dstIPv6List[ host.index ] = []
+            for correspondent in host.correspondents:
+                if not correspondent in upHosts:
+                    continue
+                for ipAddress in correspondent.ipAddresses:
+                    if ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv6Prefix' ] ) ):
+                        dstIPv6List[ host.index ].append( ipAddress )
+                    elif ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv4Prefix' ] ) ):
+                        dstIPv4List[ host.index ].append( ipAddress )
+            thread = main.Thread( target=host.handle.pingHostSetAlternative,
+                                  threadID=main.threadID,
+                                  name="pingHostSetAlternative",
+                                  args=[ dstIPv4List[ host.index ], 1 ] )
+            pool.append( thread )
+            thread.start()
+            with main.variableLock:
+                main.threadID += 1
+        for thread in pool:
+            thread.join( 10 )
+            if not thread.result:
+                checkResult = EventStates().FAIL
+                main.log.warn( "Traffic Check - ping failed" )
+
+        if not main.enableIPv6:
+            return checkResult
+        # Check ipv6 ping
+        for host in upHosts:
+            thread = main.Thread( target=host.handle.pingHostSetAlternative,
+                                  threadID=main.threadID,
+                                  name="pingHostSetAlternative",
+                                  args=[ dstIPv6List[ host.index ], 1, True ] )
+            pool.append( thread )
+            thread.start()
+            with main.variableLock:
+                main.threadID += 1
+        for thread in pool:
+            thread.join( 10 )
+            if not thread.result:
+                checkResult = EventStates().FAIL
+                main.log.warn( "Traffic Check - ping6 failed" )
+        return checkResult
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/Event.py b/TestON/tests/CHOTestMonkey/dependencies/events/Event.py
new file mode 100644
index 0000000..2abd77f
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/Event.py
@@ -0,0 +1,43 @@
+"""
+This file contains the Event class for CHOTestMonkey
+Author: you@onlab.us
+"""
+from threading import Lock
+
+class EventType:
+    def __init__( self ):
+        self.map = {}
+        # Group events (>100) should be divided into individual events by the generator before going to the scheduler
+        self.NULL = 0
+        for eventName in main.params[ 'EVENT' ].keys():
+            typeString = main.params[ 'EVENT' ][ eventName ][ 'typeString' ]
+            typeIndex = int( main.params[ 'EVENT' ][ eventName ][ 'typeIndex' ] )
+            setattr( self, typeString, typeIndex )
+            self.map[ typeIndex ] = typeString
+
+class EventStates:
+    def __init__( self ):
+        self.map = {}
+        self.FAIL = 0
+        self.map[ 0 ] = 'FAIL'
+        self.PASS = 1
+        self.map[ 1 ] = 'PASS'
+        self.ABORT = -1
+        self.map[ -1 ] = 'ABORT'
+
+class Event:
+    """
+    Event class for CHOTestMonkey
+    It is the super class for CheckEvent and NetworkEvent
+    """
+    def __init__( self ):
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+        self.eventLock = Lock()
+        self.variableLock = Lock()
+
+    def startEvent( self, args=None ):
+        """
+        Start running the event
+        """
+        return EventStates().PASS
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
new file mode 100644
index 0000000..7247ecf
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -0,0 +1,297 @@
+"""
+This file contains classes for CHOTestMonkey that are related to network event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.elements.NetworkElement import NetworkElement, Device, Host, Link
+from tests.CHOTestMonkey.dependencies.GraphHelper import GraphHelper
+
+class LinkEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        self.linkA = None
+        self.linkB = None
+
+    def startLinkEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        """
+        args are the names of the two link ends, e.g. ['s1', 's2']
+        """
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if len( args ) < 2:
+                main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                return EventStates().ABORT
+            elif len( args ) > 2:
+                main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                return EventStates().ABORT
+            if args[ 0 ] == 'random' or args[ 1 ] == 'random':
+                import random
+                if self.typeIndex == EventType().NETWORK_LINK_DOWN:
+                    with main.variableLock:
+                        graphHelper = GraphHelper()
+                        availableLinks = graphHelper.getNonCutEdges()
+                        if len( availableLinks ) == 0:
+                            main.log.warn( "All links are cut edges, aborting event" )
+                            return EventStates().ABORT
+                        linkList = random.sample( availableLinks, 1 )
+                        self.linkA = linkList[ 0 ]
+                        self.linkB = linkList[ 0 ].backwardLink
+                elif self.typeIndex == EventType().NETWORK_LINK_UP:
+                    with main.variableLock:
+                        downLinks = []
+                        for link in main.links:
+                            if link.isDown():
+                                downLinks.append( link )
+                        if len( downLinks ) == 0:
+                            main.log.warn( "None of the links are in 'down' state, aborting event" )
+                            return EventStates().ABORT
+                        linkList = random.sample( downLinks, 1 )
+                        self.linkA = linkList[ 0 ]
+                        self.linkB = linkList[ 0 ].backwardLink
+            elif args[ 0 ] == args[ 1 ]:
+                main.log.warn( "%s - invalid arguments: %s" % ( self.typeString, args ) )
+                return EventStates().ABORT
+            else:
+                for link in main.links:
+                    if link.deviceA.name == args[ 0 ] and link.deviceB.name == args[ 1 ]:
+                        self.linkA = link
+                    elif link.deviceA.name == args[ 1 ] and link.deviceB.name == args[ 0 ]:
+                        self.linkB = link
+                    if self.linkA != None and self.linkB != None:
+                        break
+                if self.linkA == None or self.linkB == None:
+                    main.log.warn( "Bidirectional link %s - %s does not exist: " % ( args[ 0 ], args[ 1 ] ) )
+                    return EventStates().ABORT
+            main.log.debug( "%s - %s" % ( self.typeString, self.linkA ) )
+            return self.startLinkEvent()
+
+class LinkDown( LinkEvent ):
+    """
+    Generate a link down event giving the two ends of the link
+    """
+    def __init__( self ):
+        LinkEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startLinkEvent( self ):
+        # TODO: do we need to handle a unidirectional link?
+        assert self.linkA != None and self.linkB != None
+        with main.variableLock:
+            if self.linkA.isDown() or self.linkB.isDown():
+                main.log.warn( "Link Down - link already down" )
+                return EventStates().ABORT
+            elif self.linkA.isRemoved() or self.linkB.isRemoved():
+                main.log.warn( "Link Down - link has been removed" )
+                return EventStates().ABORT
+        with main.mininetLock:
+            result = main.Mininet1.link( END1=self.linkA.deviceA.name,
+                                         END2=self.linkA.deviceB.name,
+                                         OPTION="down")
+        if not result:
+            main.log.warn( "%s - failed to bring down link" % ( self.typeString ) )
+            return EventStates().FAIL
+        with main.variableLock:
+            self.linkA.bringDown()
+            self.linkB.bringDown()
+        return EventStates().PASS
+
+class LinkUp( LinkEvent ):
+    """
+    Generate a link up event giving the two ends of the link
+    """
+    def __init__( self ):
+        LinkEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startLinkEvent( self ):
+        assert self.linkA != None and self.linkB != None
+        with main.variableLock:
+            if self.linkA.isUp() or self.linkB.isUp():
+                main.log.warn( "Link Up - link already up" )
+                return EventStates().ABORT
+            if self.linkA.isRemoved() or self.linkB.isRemoved():
+                main.log.warn( "Link Up - link has been removed" )
+                return EventStates().ABORT
+        with main.mininetLock:
+            result = main.Mininet1.link( END1=self.linkA.deviceA.name,
+                                         END2=self.linkA.deviceB.name,
+                                         OPTION="up")
+        if not result:
+            main.log.warn( "%s - failed to bring up link" % ( self.typeString ) )
+            return EventStates().FAIL
+        with main.variableLock:
+            self.linkA.bringUp()
+            self.linkB.bringUp()
+        return EventStates().PASS
+
+class DeviceEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        self.device = None
+
+    def startDeviceEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        """
+        args are the names of the device, e.g. 's1'
+        """
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if len( args ) < 1:
+                main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                return EventStates().ABORT
+            elif len( args ) > 1:
+                main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                return EventStates().ABORT
+            if args[ 0 ] == 'random':
+                import random
+                if self.typeIndex == EventType().NETWORK_DEVICE_DOWN:
+                    with main.variableLock:
+                        graphHelper = GraphHelper()
+                        availableDevices = graphHelper.getNonCutVertices()
+                        if len( availableDevices ) == 0:
+                            main.log.warn( "All devices are cut vertices, aborting event" )
+                            return EventStates().ABORT
+                        deviceList = random.sample( availableDevices, 1 )
+                        self.device = deviceList[ 0 ]
+                elif self.typeIndex == EventType().NETWORK_DEVICE_UP:
+                    with main.variableLock:
+                        removedDevices = []
+                        for device in main.devices:
+                            if device.isRemoved():
+                                removedDevices.append( device )
+                        if len( removedDevices ) == 0:
+                            main.log.warn( "None of the devices are removed, aborting event" )
+                            return EventStates().ABORT
+                        deviceList = random.sample( removedDevices, 1 )
+                        self.device = deviceList[ 0 ]
+            else:
+                for device in main.devices:
+                    if device.name == args[ 0 ]:
+                        self.device = device
+                if self.device == None:
+                    main.log.warn( "Device %s does not exist: " % ( args[ 0 ] ) )
+                    return EventStates().ABORT
+            main.log.debug( "%s - %s" % ( self.typeString, self.device ) )
+            return self.startDeviceEvent()
+
+class DeviceDown( DeviceEvent ):
+    """
+    Generate a device down event (which actually removes this device for now) giving its name
+    """
+    def __init__( self ):
+        DeviceEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startDeviceEvent( self ):
+        assert self.device != None
+        with main.variableLock:
+            if self.device.isRemoved():
+                main.log.warn( "Device Down - device has been removed" )
+                return EventStates().ABORT
+        with main.mininetLock:
+            result = main.Mininet1.delSwitch( self.device.name )
+        if not result:
+            main.log.warn( "%s - failed to bring down device" % ( self.typeString ) )
+            return EventStates().FAIL
+        with main.variableLock:
+            self.device.setRemoved()
+            for link in self.device.outgoingLinks:
+                link.setRemoved()
+                link.backwardLink.setRemoved()
+            for host in self.device.hosts:
+                host.setRemoved()
+            for intent in main.intents:
+                if intent.deviceA == self.device or intent.deviceB == self.device:
+                    intent.setFailed()
+        return EventStates().PASS
+
+class DeviceUp( DeviceEvent ):
+    """
+    Generate a device up event (which re-adds this device in case the device is removed) giving its name
+    """
+    def __init__( self ):
+        DeviceEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startDeviceEvent( self ):
+        assert self.device != None
+        with main.variableLock:
+            if self.device.isUp():
+                main.log.warn( "Device Up - device already up" )
+                return EventStates().ABORT
+        # Re-add the device
+        with main.mininetLock:
+            result = main.Mininet1.addSwitch( self.device.name, dpid=self.device.dpid[3:] )
+        if not result:
+            main.log.warn( "%s - failed to re-add device" % ( self.typeString ) )
+            return EventStates().FAIL
+        with main.variableLock:
+            self.device.bringUp()
+        # Re-add links
+        # We add host-device links first since we did the same in mininet topology file
+        # TODO: a more rubust way is to add links according to the port info of the device
+        for host in self.device.hosts:
+            # Add host-device link
+            with main.mininetLock:
+                result = main.Mininet1.addLink( self.device.name, host.name )
+            if not result:
+                main.log.warn( "%s - failed to re-connect host %s to device" % ( self.typeString, host.name ) )
+                return EventStates().FAIL
+        for link in self.device.outgoingLinks:
+            neighbor = link.deviceB
+            # Skip bringing up any link that connecting this device to a removed neighbor
+            if neighbor.isRemoved():
+                continue
+            with main.mininetLock:
+                result = main.Mininet1.addLink( self.device.name, neighbor.name )
+            if not result:
+                main.log.warn( "%s - failed to re-add link to %s" % ( self.typeString, neighbor.name ) )
+                return EventStates().FAIL
+            with main.variableLock:
+                link.bringUp()
+                link.backwardLink.bringUp()
+                for intent in main.intents:
+                    if intent.isFailed():
+                        if intent.deviceA == self.device and intent.deviceB.isUp() or\
+                        intent.deviceB == self.device and intent.deviceA.isUp():
+                            intent.setInstalled()
+        # Re-assign mastership for the device
+        with main.mininetLock:
+            main.Mininet1.assignSwController( sw=self.device.name, ip=main.onosIPs )
+        # Re-discover hosts
+        for host in self.device.hosts:
+            correspondent = None
+            for h in main.hosts:
+                if h.isUp() and h != host:
+                    correspondent = h
+                    break
+            if correspondent == None:
+                with main.mininetLock:
+                    main.Mininet1.pingall()
+                    if main.enableIPv6:
+                        main.Mininet1.pingall( protocol="IPv6" )
+            else:
+                ipv4Addr = None
+                ipv6Addr = None
+                for ipAddress in correspondent.ipAddresses:
+                    if ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv6Prefix' ] ) ) and ipv6Addr == None:
+                        ipv6Addr = ipAddress
+                    elif ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv4Prefix' ] ) ) and ipv4Addr == None:
+                        ipv4Addr = ipAddress
+                assert ipv4Addr != None
+                host.handle.pingHostSetAlternative( [ ipv4Addr ], 1 )
+                if main.enableIPv6:
+                    assert ipv6Addr != None
+                    host.handle.pingHostSetAlternative( [ ipv6Addr ], 1, True )
+            with main.variableLock:
+                host.bringUp()
+        return EventStates().PASS
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
new file mode 100644
index 0000000..f4e2a89
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
@@ -0,0 +1,194 @@
+"""
+This file contains classes for CHOTestMonkey that are related to application event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+
+class ONOSEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        self.ONOSIndex = -1
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            result = EventStates().PASS
+            if self.typeIndex == EventType().ONOS_ONOS_DOWN or self.typeIndex == EventType().ONOS_ONOS_UP:
+                if len( args ) < 1:
+                    main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                    result = EventStates().ABORT
+                elif len( args ) > 1:
+                    main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                    result = EventStates().ABORT
+                else:
+                    index = int( args[ 0 ] )
+                    if index < 1 or index > int( main.numCtrls ):
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        result = EventStates().ABORT
+                    else:
+                        self.ONOSIndex = index
+                        result = self.startONOSEvent()
+            return result
+
+class ONOSDown( ONOSEvent ):
+    def __init__( self ):
+        ONOSEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startONOSEvent( self ):
+        assert self.ONOSIndex != -1
+        with main.variableLock:
+            if not main.controllers[ self.ONOSIndex - 1 ].isUp():
+                main.log.warn( "ONOS Down - ONOS already down" )
+                return EventStates().ABORT
+        with main.ONOSbenchLock:
+            result = main.ONOSbench.onosStop( main.controllers[ self.ONOSIndex - 1 ].ip )
+        if not result:
+            main.log.warn( "%s - failed to bring down ONOS" % ( self.typeString ) )
+            return EventStates().FAIL
+        with main.variableLock:
+            main.controllers[ self.ONOSIndex - 1 ].bringDown()
+        return EventStates().PASS
+
+class ONOSUp( ONOSEvent ):
+    def __init__( self ):
+        ONOSEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startONOSEvent( self ):
+        assert self.ONOSIndex != -1
+        with main.variableLock:
+            if main.controllers[ self.ONOSIndex - 1 ].isUp():
+                main.log.warn( "ONOS Up - ONOS already up" )
+                return EventStates().ABORT
+        with main.ONOSbenchLock:
+            startResult = main.ONOSbench.onosStart( main.controllers[ self.ONOSIndex - 1 ].ip )
+        if not startResult:
+            main.log.warn( "%s - failed to bring up ONOS" % ( self.typeString ) )
+            return EventStates().FAIL
+        else:
+            ONOSState = main.ONOSbench.isup( main.controllers[ self.ONOSIndex - 1 ].ip )
+            if not ONOSState:
+                main.log.warn( "%s - ONOS is not up" % ( self.typeString ) )
+                return EventStates().FAIL
+            else:
+                cliResult = main.controllers[ self.ONOSIndex - 1 ].startCLI()
+                if not cliResult:
+                    main.log.warn( "%s - failed to start ONOS cli" % ( self.typeString ) )
+                    return EventStates().FAIL
+                else:
+                    with main.variableLock:
+                        main.controllers[ self.ONOSIndex - 1 ].bringUp()
+        return EventStates().PASS
+
+class CfgEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        self.component = ''
+        self.propName = ''
+        self.value = ''
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            result = self.startCfgEvent( args )
+            return result
+
+class SetCfg( CfgEvent ):
+    def __init__( self ):
+        CfgEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCfgEvent( self, args ):
+        if len( args ) < 3:
+            main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+            return EventStates().ABORT
+        elif len( args ) > 3:
+            main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+            return EventStates().ABORT
+        else:
+            self.component = str( args[ 0 ] )
+            self.propName = str( args[ 1 ] )
+            self.value = str( args[ 2 ] )
+        assert self.component != '' and self.propName != '' and self.value != ''
+        index = -1
+        for controller in main.controllers:
+            if controller.isUp():
+                index = controller.index
+        if index == -1:
+            main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+            return EventStates().ABORT
+        controller = main.controllers[ index - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.setCfg( component=self.component,
+                                            propName=self.propName,
+                                            value=self.value )
+        if not result:
+            main.log.warn( "%s - failed to set configuration" % ( self.typeString ) )
+            return EventStates().FAIL
+        return EventStates().PASS
+
+class SetFlowObj( CfgEvent ):
+    def __init__( self ):
+        CfgEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startCfgEvent( self, args ):
+        if len( args ) < 1:
+            main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+            return EventStates().ABORT
+        elif len( args ) > 1:
+            main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+            return EventStates().ABORT
+        elif args[ 0 ] != 'true' and args[ 0 ] != 'false':
+            main.log.warn( "%s - Invalid arguments: %s" % ( self.typeString, args ) )
+            return EventStates().ABORT
+        else:
+            self.component = 'org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator'
+            self.propName = 'useFlowObjectives'
+            self.value = str( args[ 0 ] )
+        index = -1
+        for controller in main.controllers:
+            if controller.isUp():
+                index = controller.index
+        if index == -1:
+            main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+            return EventStates().ABORT
+        controller = main.controllers[ index - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.setCfg( component=self.component,
+                                            propName=self.propName,
+                                            value=self.value )
+        if not result:
+            main.log.warn( "%s - failed to set configuration" % ( self.typeString ) )
+            return EventStates().FAIL
+        return EventStates().PASS
+
+class BalanceMasters( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startEvent( self, args=None ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            index = -1
+            for controller in main.controllers:
+                if controller.isUp():
+                    index = controller.index
+            if index == -1:
+                main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+                return EventStates().ABORT
+            controller = main.controllers[ index - 1 ]
+            with controller.CLILock:
+                result = controller.CLI.balanceMasters()
+            if not result:
+                main.log.warn( "%s - failed to balance masters" % ( self.typeString ) )
+                return EventStates().FAIL
+            return EventStates().PASS
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py
new file mode 100644
index 0000000..605e43f
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py
@@ -0,0 +1,59 @@
+"""
+This file contains classes for CHOTestMonkey that are related to check event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+
+class TestEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+
+    def startTestEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            result = self.startTestEvent( args )
+            return result
+
+class TestPause( TestEvent ):
+    def __init__( self ):
+        TestEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startTestEvent( self, args=None ):
+        result = EventStates().PASS
+        main.eventScheduler.setRunningState( False )
+        return result
+
+class TestResume( TestEvent ):
+    def __init__( self ):
+        TestEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startTestEvent( self, args=None ):
+        result = EventStates().PASS
+        main.eventScheduler.setRunningState( True )
+        return result
+
+class TestSleep( TestEvent ):
+    def __init__( self ):
+        TestEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startTestEvent( self, args ):
+        import time
+        result = EventStates().PASS
+        if len( args ) < 1:
+            main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+            result = EventStates().ABORT
+        elif len( args ) > 1:
+            main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+            result = EventStates().ABORT
+        sleepTime = int( args[ 0 ] )
+        time.sleep( sleepTime )
+        return result
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/CHOTestMonkey/dependencies/events/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/CHOTestMonkey/dependencies/events/__init__.py
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/CHOTestMonkey/dependencies/topologies/__init__.py
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAtt.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAtt.py
new file mode 100755
index 0000000..4291f8b
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAtt.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+
+"""
+Custom topology for Mininet
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class attTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        # add nodes, switches first...
+        NY54 = self.addSwitch( 's1' )
+        CMBR = self.addSwitch( 's2' )
+        CHCG = self.addSwitch( 's3' )
+        CLEV = self.addSwitch( 's4' )
+        RLGH = self.addSwitch( 's5' )
+        ATLN = self.addSwitch( 's6' )
+        PHLA = self.addSwitch( 's7' )
+        WASH = self.addSwitch( 's8' )
+        NSVL = self.addSwitch( 's9' )
+        STLS = self.addSwitch( 's10' )
+        NWOR = self.addSwitch( 's11' )
+        HSTN = self.addSwitch( 's12' )
+        SNAN = self.addSwitch( 's13' )
+        DLLS = self.addSwitch( 's14' )
+        ORLD = self.addSwitch( 's15' )
+        DNVR = self.addSwitch( 's16' )
+        KSCY = self.addSwitch( 's17' )
+        SNFN = self.addSwitch( 's18' )
+        SCRM = self.addSwitch( 's19' )
+        PTLD = self.addSwitch( 's20' )
+        STTL = self.addSwitch( 's21' )
+        SLKC = self.addSwitch( 's22' )
+        LA03 = self.addSwitch( 's23' )
+        SNDG = self.addSwitch( 's24' )
+        PHNX = self.addSwitch( 's25' )
+
+        # ... and now hosts
+        NY54_host = self.addHost( 'h1' )
+        CMBR_host = self.addHost( 'h2' )
+        CHCG_host = self.addHost( 'h3' )
+        CLEV_host = self.addHost( 'h4' )
+        RLGH_host = self.addHost( 'h5' )
+        ATLN_host = self.addHost( 'h6' )
+        PHLA_host = self.addHost( 'h7' )
+        WASH_host = self.addHost( 'h8' )
+        NSVL_host = self.addHost( 'h9' )
+        STLS_host = self.addHost( 'h10' )
+        NWOR_host = self.addHost( 'h11' )
+        HSTN_host = self.addHost( 'h12' )
+        SNAN_host = self.addHost( 'h13' )
+        DLLS_host = self.addHost( 'h14' )
+        ORLD_host = self.addHost( 'h15' )
+        DNVR_host = self.addHost( 'h16' )
+        KSCY_host = self.addHost( 'h17' )
+        SNFN_host = self.addHost( 'h18' )
+        SCRM_host = self.addHost( 'h19' )
+        PTLD_host = self.addHost( 'h20' )
+        STTL_host = self.addHost( 'h21' )
+        SLKC_host = self.addHost( 'h22' )
+        LA03_host = self.addHost( 'h23' )
+        SNDG_host = self.addHost( 'h24' )
+        PHNX_host = self.addHost( 'h25' )
+
+        # add edges between switch and corresponding host
+        self.addLink( NY54 , NY54_host )
+        self.addLink( CMBR , CMBR_host )
+        self.addLink( CHCG , CHCG_host )
+        self.addLink( CLEV , CLEV_host )
+        self.addLink( RLGH , RLGH_host )
+        self.addLink( ATLN , ATLN_host )
+        self.addLink( PHLA , PHLA_host )
+        self.addLink( WASH , WASH_host )
+        self.addLink( NSVL , NSVL_host )
+        self.addLink( STLS , STLS_host )
+        self.addLink( NWOR , NWOR_host )
+        self.addLink( HSTN , HSTN_host )
+        self.addLink( SNAN , SNAN_host )
+        self.addLink( DLLS , DLLS_host )
+        self.addLink( ORLD , ORLD_host )
+        self.addLink( DNVR , DNVR_host )
+        self.addLink( KSCY , KSCY_host )
+        self.addLink( SNFN , SNFN_host )
+        self.addLink( SCRM , SCRM_host )
+        self.addLink( PTLD , PTLD_host )
+        self.addLink( STTL , STTL_host )
+        self.addLink( SLKC , SLKC_host )
+        self.addLink( LA03 , LA03_host )
+        self.addLink( SNDG , SNDG_host )
+        self.addLink( PHNX , PHNX_host )
+
+        # add edges between switches
+        self.addLink( NY54 , CMBR, bw=10, delay='0.979030824185ms')
+        self.addLink( NY54 , CHCG, bw=10, delay='0.806374975652ms')
+        self.addLink( NY54 , PHLA, bw=10, delay='0.686192970166ms')
+        self.addLink( NY54 , WASH, bw=10, delay='0.605826192092ms')
+        self.addLink( CMBR , PHLA, bw=10, delay='1.4018238197ms')
+        self.addLink( CHCG , CLEV, bw=10, delay='0.232315346482ms')
+        self.addLink( CHCG , PHLA, bw=10, delay='1.07297714274ms')
+        self.addLink( CHCG , STLS, bw=10, delay='1.12827896944ms')
+        self.addLink( CHCG , DNVR, bw=10, delay='1.35964770335ms')
+        self.addLink( CHCG , KSCY, bw=10, delay='1.5199778541ms')
+        self.addLink( CHCG , SNFN, bw=10, delay='0.620743405435ms')
+        self.addLink( CHCG , STTL, bw=10, delay='0.93027212534ms')
+        self.addLink( CHCG , SLKC, bw=10, delay='0.735621751348ms')
+        self.addLink( CLEV , NSVL, bw=10, delay='0.523419372248ms')
+        self.addLink( CLEV , STLS, bw=10, delay='1.00360290845ms')
+        self.addLink( CLEV , PHLA, bw=10, delay='0.882912133249ms')
+        self.addLink( RLGH , ATLN, bw=10, delay='1.1644489729ms')
+        self.addLink( RLGH , WASH, bw=10, delay='1.48176810502ms')
+        self.addLink( ATLN , WASH, bw=10, delay='0.557636936322ms')
+        self.addLink( ATLN , NSVL, bw=10, delay='1.32869749865ms')
+        self.addLink( ATLN , STLS, bw=10, delay='0.767705554748ms')
+        self.addLink( ATLN , DLLS, bw=10, delay='0.544782086448ms')
+        self.addLink( ATLN , ORLD, bw=10, delay='1.46119152532ms')
+        self.addLink( PHLA , WASH, bw=10, delay='0.372209320106ms')
+        self.addLink( NSVL , STLS, bw=10, delay='1.43250491305ms')
+        self.addLink( NSVL , DLLS, bw=10, delay='1.67698215288ms')
+        self.addLink( STLS , DLLS, bw=10, delay='0.256389964194ms')
+        self.addLink( STLS , KSCY, bw=10, delay='0.395511571791ms')
+        self.addLink( STLS , LA03, bw=10, delay='0.257085227363ms')
+        self.addLink( NWOR , HSTN, bw=10, delay='0.0952906633914ms')
+        self.addLink( NWOR , DLLS, bw=10, delay='1.60231329739ms')
+        self.addLink( NWOR , ORLD, bw=10, delay='0.692731063896ms')
+        self.addLink( HSTN , SNAN, bw=10, delay='0.284150653798ms')
+        self.addLink( HSTN , DLLS, bw=10, delay='1.65690128332ms')
+        self.addLink( HSTN , ORLD, bw=10, delay='0.731886304782ms')
+        self.addLink( SNAN , PHNX, bw=10, delay='1.34258627257ms')
+        self.addLink( SNAN , DLLS, bw=10, delay='1.50063532341ms')
+        self.addLink( DLLS , DNVR, bw=10, delay='0.251471593235ms')
+        self.addLink( DLLS , KSCY, bw=10, delay='0.18026026737ms')
+        self.addLink( DLLS , SNFN, bw=10, delay='0.74304274592ms')
+        self.addLink( DLLS , LA03, bw=10, delay='0.506439293357ms')
+        self.addLink( DNVR , KSCY, bw=10, delay='0.223328790403ms')
+        self.addLink( DNVR , SNFN, bw=10, delay='0.889017541903ms')
+        self.addLink( DNVR , SLKC, bw=10, delay='0.631898982721ms')
+        self.addLink( KSCY , SNFN, bw=10, delay='0.922778522233ms')
+        self.addLink( SNFN , SCRM, bw=10, delay='0.630352278097ms')
+        self.addLink( SNFN , PTLD, bw=10, delay='0.828572513655ms')
+        self.addLink( SNFN , STTL, bw=10, delay='1.54076081649ms')
+        self.addLink( SNFN , SLKC, bw=10, delay='0.621507502625ms')
+        self.addLink( SNFN , LA03, bw=10, delay='0.602936230151ms')
+        self.addLink( SCRM , SLKC, bw=10, delay='0.461350343644ms')
+        self.addLink( PTLD , STTL, bw=10, delay='1.17591515181ms')
+        self.addLink( SLKC , LA03, bw=10, delay='0.243225267023ms')
+        self.addLink( LA03 , SNDG, bw=10, delay='0.681264950821ms')
+        self.addLink( LA03 , PHNX, bw=10, delay='0.343709457969ms')
+        self.addLink( SNDG , PHNX, bw=10, delay='0.345064487693ms')
+
+topos = { 'att': ( lambda: attTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = attTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, link=TCLink, autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAttIpv6.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAttIpv6.py
new file mode 100755
index 0000000..f135c81
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoAttIpv6.py
@@ -0,0 +1,188 @@
+#!/usr/bin/python
+
+"""
+Custom topology for Mininet
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Host, RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class dualStackHost( Host ):
+    def config( self, v6Addr='1000::1/64', **params ):
+        r = super( Host, self ).config( **params )
+        intf = self.defaultIntf()
+        self.cmd( 'ip -6 addr add %s dev %s' % ( v6Addr, intf ) )
+        return r
+
+class attTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        NY54 = self.addSwitch( 's1' )
+        CMBR = self.addSwitch( 's2' )
+        CHCG = self.addSwitch( 's3' )
+        CLEV = self.addSwitch( 's4' )
+        RLGH = self.addSwitch( 's5' )
+        ATLN = self.addSwitch( 's6' )
+        PHLA = self.addSwitch( 's7' )
+        WASH = self.addSwitch( 's8' )
+        NSVL = self.addSwitch( 's9' )
+        STLS = self.addSwitch( 's10' )
+        NWOR = self.addSwitch( 's11' )
+        HSTN = self.addSwitch( 's12' )
+        SNAN = self.addSwitch( 's13' )
+        DLLS = self.addSwitch( 's14' )
+        ORLD = self.addSwitch( 's15' )
+        DNVR = self.addSwitch( 's16' )
+        KSCY = self.addSwitch( 's17' )
+        SNFN = self.addSwitch( 's18' )
+        SCRM = self.addSwitch( 's19' )
+        PTLD = self.addSwitch( 's20' )
+        STTL = self.addSwitch( 's21' )
+        SLKC = self.addSwitch( 's22' )
+        LA03 = self.addSwitch( 's23' )
+        SNDG = self.addSwitch( 's24' )
+        PHNX = self.addSwitch( 's25' )
+
+        # ... and now hosts
+        NY54_host = self.addHost( 'h1', ip='10.1.0.1/24', cls=dualStackHost, v6Addr='1000::1/64' )
+        CMBR_host = self.addHost( 'h2', ip='10.1.0.2/24', cls=dualStackHost, v6Addr='1000::2/64' )
+        CHCG_host = self.addHost( 'h3', ip='10.1.0.3/24', cls=dualStackHost, v6Addr='1000::3/64' )
+        CLEV_host = self.addHost( 'h4', ip='10.1.0.4/24', cls=dualStackHost, v6Addr='1000::4/64' )
+        RLGH_host = self.addHost( 'h5', ip='10.1.0.5/24', cls=dualStackHost, v6Addr='1000::5/64' )
+        ATLN_host = self.addHost( 'h6', ip='10.1.0.6/24', cls=dualStackHost, v6Addr='1000::6/64' )
+        PHLA_host = self.addHost( 'h7', ip='10.1.0.7/24', cls=dualStackHost, v6Addr='1000::7/64' )
+        WASH_host = self.addHost( 'h8', ip='10.1.0.8/24', cls=dualStackHost, v6Addr='1000::8/64' )
+        NSVL_host = self.addHost( 'h9', ip='10.1.0.9/24', cls=dualStackHost, v6Addr='1000::9/64' )
+        STLS_host = self.addHost( 'h10', ip='10.1.0.10/24', cls=dualStackHost, v6Addr='1000::10/64' )
+        NWOR_host = self.addHost( 'h11', ip='10.1.0.11/24', cls=dualStackHost, v6Addr='1000::11/64' )
+        HSTN_host = self.addHost( 'h12', ip='10.1.0.12/24', cls=dualStackHost, v6Addr='1000::12/64' )
+        SNAN_host = self.addHost( 'h13', ip='10.1.0.13/24', cls=dualStackHost, v6Addr='1000::13/64' )
+        DLLS_host = self.addHost( 'h14', ip='10.1.0.14/24', cls=dualStackHost, v6Addr='1000::14/64' )
+        ORLD_host = self.addHost( 'h15', ip='10.1.0.15/24', cls=dualStackHost, v6Addr='1000::15/64' )
+        DNVR_host = self.addHost( 'h16', ip='10.1.0.16/24', cls=dualStackHost, v6Addr='1000::16/64' )
+        KSCY_host = self.addHost( 'h17', ip='10.1.0.17/24', cls=dualStackHost, v6Addr='1000::17/64' )
+        SNFN_host = self.addHost( 'h18', ip='10.1.0.18/24', cls=dualStackHost, v6Addr='1000::18/64' )
+        SCRM_host = self.addHost( 'h19', ip='10.1.0.19/24', cls=dualStackHost, v6Addr='1000::19/64' )
+        PTLD_host = self.addHost( 'h20', ip='10.1.0.20/24', cls=dualStackHost, v6Addr='1000::20/64' )
+        STTL_host = self.addHost( 'h21', ip='10.1.0.21/24', cls=dualStackHost, v6Addr='1000::21/64' )
+        SLKC_host = self.addHost( 'h22', ip='10.1.0.22/24', cls=dualStackHost, v6Addr='1000::22/64' )
+        LA03_host = self.addHost( 'h23', ip='10.1.0.23/24', cls=dualStackHost, v6Addr='1000::23/64' )
+        SNDG_host = self.addHost( 'h24', ip='10.1.0.24/24', cls=dualStackHost, v6Addr='1000::24/64' )
+        PHNX_host = self.addHost( 'h25', ip='10.1.0.25/24', cls=dualStackHost, v6Addr='1000::25/64' )
+
+        # add edges between switch and corresponding host
+        self.addLink( NY54 , NY54_host )
+        self.addLink( CMBR , CMBR_host )
+        self.addLink( CHCG , CHCG_host )
+        self.addLink( CLEV , CLEV_host )
+        self.addLink( RLGH , RLGH_host )
+        self.addLink( ATLN , ATLN_host )
+        self.addLink( PHLA , PHLA_host )
+        self.addLink( WASH , WASH_host )
+        self.addLink( NSVL , NSVL_host )
+        self.addLink( STLS , STLS_host )
+        self.addLink( NWOR , NWOR_host )
+        self.addLink( HSTN , HSTN_host )
+        self.addLink( SNAN , SNAN_host )
+        self.addLink( DLLS , DLLS_host )
+        self.addLink( ORLD , ORLD_host )
+        self.addLink( DNVR , DNVR_host )
+        self.addLink( KSCY , KSCY_host )
+        self.addLink( SNFN , SNFN_host )
+        self.addLink( SCRM , SCRM_host )
+        self.addLink( PTLD , PTLD_host )
+        self.addLink( STTL , STTL_host )
+        self.addLink( SLKC , SLKC_host )
+        self.addLink( LA03 , LA03_host )
+        self.addLink( SNDG , SNDG_host )
+        self.addLink( PHNX , PHNX_host )
+
+        # add edges between switches
+        self.addLink( NY54 , CMBR, bw=10, delay='0.979030824185ms')
+        self.addLink( NY54 , CHCG, bw=10, delay='0.806374975652ms')
+        self.addLink( NY54 , PHLA, bw=10, delay='0.686192970166ms')
+        self.addLink( NY54 , WASH, bw=10, delay='0.605826192092ms')
+        self.addLink( CMBR , PHLA, bw=10, delay='1.4018238197ms')
+        self.addLink( CHCG , CLEV, bw=10, delay='0.232315346482ms')
+        self.addLink( CHCG , PHLA, bw=10, delay='1.07297714274ms')
+        self.addLink( CHCG , STLS, bw=10, delay='1.12827896944ms')
+        self.addLink( CHCG , DNVR, bw=10, delay='1.35964770335ms')
+        self.addLink( CHCG , KSCY, bw=10, delay='1.5199778541ms')
+        self.addLink( CHCG , SNFN, bw=10, delay='0.620743405435ms')
+        self.addLink( CHCG , STTL, bw=10, delay='0.93027212534ms')
+        self.addLink( CHCG , SLKC, bw=10, delay='0.735621751348ms')
+        self.addLink( CLEV , NSVL, bw=10, delay='0.523419372248ms')
+        self.addLink( CLEV , STLS, bw=10, delay='1.00360290845ms')
+        self.addLink( CLEV , PHLA, bw=10, delay='0.882912133249ms')
+        self.addLink( RLGH , ATLN, bw=10, delay='1.1644489729ms')
+        self.addLink( RLGH , WASH, bw=10, delay='1.48176810502ms')
+        self.addLink( ATLN , WASH, bw=10, delay='0.557636936322ms')
+        self.addLink( ATLN , NSVL, bw=10, delay='1.32869749865ms')
+        self.addLink( ATLN , STLS, bw=10, delay='0.767705554748ms')
+        self.addLink( ATLN , DLLS, bw=10, delay='0.544782086448ms')
+        self.addLink( ATLN , ORLD, bw=10, delay='1.46119152532ms')
+        self.addLink( PHLA , WASH, bw=10, delay='0.372209320106ms')
+        self.addLink( NSVL , STLS, bw=10, delay='1.43250491305ms')
+        self.addLink( NSVL , DLLS, bw=10, delay='1.67698215288ms')
+        self.addLink( STLS , DLLS, bw=10, delay='0.256389964194ms')
+        self.addLink( STLS , KSCY, bw=10, delay='0.395511571791ms')
+        self.addLink( STLS , LA03, bw=10, delay='0.257085227363ms')
+        self.addLink( NWOR , HSTN, bw=10, delay='0.0952906633914ms')
+        self.addLink( NWOR , DLLS, bw=10, delay='1.60231329739ms')
+        self.addLink( NWOR , ORLD, bw=10, delay='0.692731063896ms')
+        self.addLink( HSTN , SNAN, bw=10, delay='0.284150653798ms')
+        self.addLink( HSTN , DLLS, bw=10, delay='1.65690128332ms')
+        self.addLink( HSTN , ORLD, bw=10, delay='0.731886304782ms')
+        self.addLink( SNAN , PHNX, bw=10, delay='1.34258627257ms')
+        self.addLink( SNAN , DLLS, bw=10, delay='1.50063532341ms')
+        self.addLink( DLLS , DNVR, bw=10, delay='0.251471593235ms')
+        self.addLink( DLLS , KSCY, bw=10, delay='0.18026026737ms')
+        self.addLink( DLLS , SNFN, bw=10, delay='0.74304274592ms')
+        self.addLink( DLLS , LA03, bw=10, delay='0.506439293357ms')
+        self.addLink( DNVR , KSCY, bw=10, delay='0.223328790403ms')
+        self.addLink( DNVR , SNFN, bw=10, delay='0.889017541903ms')
+        self.addLink( DNVR , SLKC, bw=10, delay='0.631898982721ms')
+        self.addLink( KSCY , SNFN, bw=10, delay='0.922778522233ms')
+        self.addLink( SNFN , SCRM, bw=10, delay='0.630352278097ms')
+        self.addLink( SNFN , PTLD, bw=10, delay='0.828572513655ms')
+        self.addLink( SNFN , STTL, bw=10, delay='1.54076081649ms')
+        self.addLink( SNFN , SLKC, bw=10, delay='0.621507502625ms')
+        self.addLink( SNFN , LA03, bw=10, delay='0.602936230151ms')
+        self.addLink( SCRM , SLKC, bw=10, delay='0.461350343644ms')
+        self.addLink( PTLD , STTL, bw=10, delay='1.17591515181ms')
+        self.addLink( SLKC , LA03, bw=10, delay='0.243225267023ms')
+        self.addLink( LA03 , SNDG, bw=10, delay='0.681264950821ms')
+        self.addLink( LA03 , PHNX, bw=10, delay='0.343709457969ms')
+        self.addLink( SNDG , PHNX, bw=10, delay='0.345064487693ms')
+
+topos = { 'att': ( lambda: attTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = attTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, link=TCLink, autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordal.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordal.py
new file mode 100755
index 0000000..542c3ed
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordal.py
@@ -0,0 +1,422 @@
+#!/usr/bin/python
+"""
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class chordalTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        # add nodes, switches first...
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+        s4 = self.addSwitch( 's4' )
+        s5 = self.addSwitch( 's5' )
+        s6 = self.addSwitch( 's6' )
+        s7 = self.addSwitch( 's7' )
+        s8 = self.addSwitch( 's8' )
+        s9 = self.addSwitch( 's9' )
+        s10 = self.addSwitch( 's10' )
+        s11 = self.addSwitch( 's11' )
+        s12 = self.addSwitch( 's12' )
+        s13 = self.addSwitch( 's13' )
+        s14 = self.addSwitch( 's14' )
+        s15 = self.addSwitch( 's15' )
+        s16 = self.addSwitch( 's16' )
+        s17 = self.addSwitch( 's17' )
+        s18 = self.addSwitch( 's18' )
+        s19 = self.addSwitch( 's19' )
+        s20 = self.addSwitch( 's20' )
+        s21 = self.addSwitch( 's21' )
+        s22 = self.addSwitch( 's22' )
+        s23 = self.addSwitch( 's23' )
+        s24 = self.addSwitch( 's24' )
+        s25 = self.addSwitch( 's25' )
+
+        # ... and now hosts
+        s1_host = self.addHost( 'h1' )
+        s2_host = self.addHost( 'h2' )
+        s3_host = self.addHost( 'h3' )
+        s4_host = self.addHost( 'h4' )
+        s5_host = self.addHost( 'h5' )
+        s6_host = self.addHost( 'h6' )
+        s7_host = self.addHost( 'h7' )
+        s8_host = self.addHost( 'h8' )
+        s9_host = self.addHost( 'h9' )
+        s10_host = self.addHost( 'h10' )
+        s11_host = self.addHost( 'h11' )
+        s12_host = self.addHost( 'h12' )
+        s13_host = self.addHost( 'h13' )
+        s14_host = self.addHost( 'h14' )
+        s15_host = self.addHost( 'h15' )
+        s16_host = self.addHost( 'h16' )
+        s17_host = self.addHost( 'h17' )
+        s18_host = self.addHost( 'h18' )
+        s19_host = self.addHost( 'h19' )
+        s20_host = self.addHost( 'h20' )
+        s21_host = self.addHost( 'h21' )
+        s22_host = self.addHost( 'h22' )
+        s23_host = self.addHost( 'h23' )
+        s24_host = self.addHost( 'h24' )
+        s25_host = self.addHost( 'h25' )
+
+        # add edges between switch and corresponding host
+        self.addLink( s1 , s1_host )
+        self.addLink( s2 , s2_host )
+        self.addLink( s3 , s3_host )
+        self.addLink( s4 , s4_host )
+        self.addLink( s5 , s5_host )
+        self.addLink( s6 , s6_host )
+        self.addLink( s7 , s7_host )
+        self.addLink( s8 , s8_host )
+        self.addLink( s9 , s9_host )
+        self.addLink( s10 , s10_host )
+        self.addLink( s11 , s11_host )
+        self.addLink( s12 , s12_host )
+        self.addLink( s13 , s13_host )
+        self.addLink( s14 , s14_host )
+        self.addLink( s15 , s15_host )
+        self.addLink( s16 , s16_host )
+        self.addLink( s17 , s17_host )
+        self.addLink( s18 , s18_host )
+        self.addLink( s19 , s19_host )
+        self.addLink( s20 , s20_host )
+        self.addLink( s21 , s21_host )
+        self.addLink( s22 , s22_host )
+        self.addLink( s23 , s23_host )
+        self.addLink( s24 , s24_host )
+        self.addLink( s25 , s25_host )
+        self.addLink(s1, s2)
+        self.addLink(s1, s3)
+        self.addLink(s1, s4)
+        self.addLink(s1, s5)
+        self.addLink(s1, s6)
+        self.addLink(s1, s7)
+        self.addLink(s1, s8)
+        self.addLink(s1, s9)
+        self.addLink(s1, s10)
+        self.addLink(s1, s11)
+        self.addLink(s1, s12)
+        self.addLink(s1, s13)
+        self.addLink(s1, s14)
+        self.addLink(s1, s15)
+        self.addLink(s1, s16)
+        self.addLink(s1, s17)
+        self.addLink(s1, s18)
+        self.addLink(s1, s19)
+        self.addLink(s1, s20)
+        self.addLink(s1, s21)
+        self.addLink(s1, s22)
+        self.addLink(s1, s23)
+        self.addLink(s1, s24)
+        self.addLink(s1, s25)
+        self.addLink(s2, s3)
+        self.addLink(s2, s4)
+        self.addLink(s2, s5)
+        self.addLink(s2, s6)
+        self.addLink(s2, s7)
+        self.addLink(s2, s8)
+        self.addLink(s2, s9)
+        self.addLink(s2, s10)
+        self.addLink(s2, s11)
+        self.addLink(s2, s12)
+        self.addLink(s2, s13)
+        self.addLink(s2, s14)
+        self.addLink(s2, s15)
+        self.addLink(s2, s16)
+        self.addLink(s2, s17)
+        self.addLink(s2, s18)
+        self.addLink(s2, s19)
+        self.addLink(s2, s20)
+        self.addLink(s2, s21)
+        self.addLink(s2, s22)
+        self.addLink(s2, s23)
+        self.addLink(s2, s24)
+        self.addLink(s2, s25)
+        self.addLink(s3, s4)
+        self.addLink(s3, s5)
+        self.addLink(s3, s6)
+        self.addLink(s3, s7)
+        self.addLink(s3, s8)
+        self.addLink(s3, s9)
+        self.addLink(s3, s10)
+        self.addLink(s3, s11)
+        self.addLink(s3, s12)
+        self.addLink(s3, s13)
+        self.addLink(s3, s14)
+        self.addLink(s3, s15)
+        self.addLink(s3, s16)
+        self.addLink(s3, s17)
+        self.addLink(s3, s18)
+        self.addLink(s3, s19)
+        self.addLink(s3, s20)
+        self.addLink(s3, s21)
+        self.addLink(s3, s22)
+        self.addLink(s3, s23)
+        self.addLink(s3, s24)
+        self.addLink(s3, s25)
+        self.addLink(s4, s5)
+        self.addLink(s4, s6)
+        self.addLink(s4, s7)
+        self.addLink(s4, s8)
+        self.addLink(s4, s9)
+        self.addLink(s4, s10)
+        self.addLink(s4, s11)
+        self.addLink(s4, s12)
+        self.addLink(s4, s13)
+        self.addLink(s4, s14)
+        self.addLink(s4, s15)
+        self.addLink(s4, s16)
+        self.addLink(s4, s17)
+        self.addLink(s4, s18)
+        self.addLink(s4, s19)
+        self.addLink(s4, s20)
+        self.addLink(s4, s21)
+        self.addLink(s4, s22)
+        self.addLink(s4, s23)
+        self.addLink(s4, s24)
+        self.addLink(s4, s25)
+        self.addLink(s5, s6)
+        self.addLink(s5, s7)
+        self.addLink(s5, s8)
+        self.addLink(s5, s9)
+        self.addLink(s5, s10)
+        self.addLink(s5, s11)
+        self.addLink(s5, s12)
+        self.addLink(s5, s13)
+        self.addLink(s5, s14)
+        self.addLink(s5, s15)
+        self.addLink(s5, s16)
+        self.addLink(s5, s17)
+        self.addLink(s5, s18)
+        self.addLink(s5, s19)
+        self.addLink(s5, s20)
+        self.addLink(s5, s21)
+        self.addLink(s5, s22)
+        self.addLink(s5, s23)
+        self.addLink(s5, s24)
+        self.addLink(s5, s25)
+        self.addLink(s6, s7)
+        self.addLink(s6, s8)
+        self.addLink(s6, s9)
+        self.addLink(s6, s10)
+        self.addLink(s6, s11)
+        self.addLink(s6, s12)
+        self.addLink(s6, s13)
+        self.addLink(s6, s14)
+        self.addLink(s6, s15)
+        self.addLink(s6, s16)
+        self.addLink(s6, s17)
+        self.addLink(s6, s18)
+        self.addLink(s6, s19)
+        self.addLink(s6, s20)
+        self.addLink(s6, s21)
+        self.addLink(s6, s22)
+        self.addLink(s6, s23)
+        self.addLink(s6, s24)
+        self.addLink(s6, s25)
+        self.addLink(s7, s8)
+        self.addLink(s7, s9)
+        self.addLink(s7, s10)
+        self.addLink(s7, s11)
+        self.addLink(s7, s12)
+        self.addLink(s7, s13)
+        self.addLink(s7, s14)
+        self.addLink(s7, s15)
+        self.addLink(s7, s16)
+        self.addLink(s7, s17)
+        self.addLink(s7, s18)
+        self.addLink(s7, s19)
+        self.addLink(s7, s20)
+        self.addLink(s7, s21)
+        self.addLink(s7, s22)
+        self.addLink(s7, s23)
+        self.addLink(s7, s24)
+        self.addLink(s7, s25)
+        self.addLink(s8, s9)
+        self.addLink(s8, s10)
+        self.addLink(s8, s11)
+        self.addLink(s8, s12)
+        self.addLink(s8, s13)
+        self.addLink(s8, s14)
+        self.addLink(s8, s15)
+        self.addLink(s8, s16)
+        self.addLink(s8, s17)
+        self.addLink(s8, s18)
+        self.addLink(s8, s19)
+        self.addLink(s8, s20)
+        self.addLink(s8, s21)
+        self.addLink(s8, s22)
+        self.addLink(s8, s23)
+        self.addLink(s8, s24)
+        self.addLink(s8, s25)
+        self.addLink(s9, s10)
+        self.addLink(s9, s11)
+        self.addLink(s9, s12)
+        self.addLink(s9, s13)
+        self.addLink(s9, s14)
+        self.addLink(s9, s15)
+        self.addLink(s9, s16)
+        self.addLink(s9, s17)
+        self.addLink(s9, s18)
+        self.addLink(s9, s19)
+        self.addLink(s9, s20)
+        self.addLink(s9, s21)
+        self.addLink(s9, s22)
+        self.addLink(s9, s23)
+        self.addLink(s9, s24)
+        self.addLink(s9, s25)
+        self.addLink(s10, s11)
+        self.addLink(s10, s12)
+        self.addLink(s10, s13)
+        self.addLink(s10, s14)
+        self.addLink(s10, s15)
+        self.addLink(s10, s16)
+        self.addLink(s10, s17)
+        self.addLink(s10, s18)
+        self.addLink(s10, s19)
+        self.addLink(s10, s20)
+        self.addLink(s10, s21)
+        self.addLink(s10, s22)
+        self.addLink(s10, s23)
+        self.addLink(s10, s24)
+        self.addLink(s10, s25)
+        self.addLink(s11, s12)
+        self.addLink(s11, s13)
+        self.addLink(s11, s14)
+        self.addLink(s11, s15)
+        self.addLink(s11, s16)
+        self.addLink(s11, s17)
+        self.addLink(s11, s18)
+        self.addLink(s11, s19)
+        self.addLink(s11, s20)
+        self.addLink(s11, s21)
+        self.addLink(s11, s22)
+        self.addLink(s11, s23)
+        self.addLink(s11, s24)
+        self.addLink(s11, s25)
+        self.addLink(s12, s13)
+        self.addLink(s12, s14)
+        self.addLink(s12, s15)
+        self.addLink(s12, s16)
+        self.addLink(s12, s17)
+        self.addLink(s12, s18)
+        self.addLink(s12, s19)
+        self.addLink(s12, s20)
+        self.addLink(s12, s21)
+        self.addLink(s12, s22)
+        self.addLink(s12, s23)
+        self.addLink(s12, s24)
+        self.addLink(s12, s25)
+        self.addLink(s13, s14)
+        self.addLink(s13, s15)
+        self.addLink(s13, s16)
+        self.addLink(s13, s17)
+        self.addLink(s13, s18)
+        self.addLink(s13, s19)
+        self.addLink(s13, s20)
+        self.addLink(s13, s21)
+        self.addLink(s13, s22)
+        self.addLink(s13, s23)
+        self.addLink(s13, s24)
+        self.addLink(s13, s25)
+        self.addLink(s14, s15)
+        self.addLink(s14, s16)
+        self.addLink(s14, s17)
+        self.addLink(s14, s18)
+        self.addLink(s14, s19)
+        self.addLink(s14, s20)
+        self.addLink(s14, s21)
+        self.addLink(s14, s22)
+        self.addLink(s14, s23)
+        self.addLink(s14, s24)
+        self.addLink(s14, s25)
+        self.addLink(s15, s16)
+        self.addLink(s15, s17)
+        self.addLink(s15, s18)
+        self.addLink(s15, s19)
+        self.addLink(s15, s20)
+        self.addLink(s15, s21)
+        self.addLink(s15, s22)
+        self.addLink(s15, s23)
+        self.addLink(s15, s24)
+        self.addLink(s15, s25)
+        self.addLink(s16, s17)
+        self.addLink(s16, s18)
+        self.addLink(s16, s19)
+        self.addLink(s16, s20)
+        self.addLink(s16, s21)
+        self.addLink(s16, s22)
+        self.addLink(s16, s23)
+        self.addLink(s16, s24)
+        self.addLink(s16, s25)
+        self.addLink(s17, s18)
+        self.addLink(s17, s19)
+        self.addLink(s17, s20)
+        self.addLink(s17, s21)
+        self.addLink(s17, s22)
+        self.addLink(s17, s23)
+        self.addLink(s17, s24)
+        self.addLink(s17, s25)
+        self.addLink(s18, s19)
+        self.addLink(s18, s20)
+        self.addLink(s18, s21)
+        self.addLink(s18, s22)
+        self.addLink(s18, s23)
+        self.addLink(s18, s24)
+        self.addLink(s18, s25)
+        self.addLink(s19, s20)
+        self.addLink(s19, s21)
+        self.addLink(s19, s22)
+        self.addLink(s19, s23)
+        self.addLink(s19, s24)
+        self.addLink(s19, s25)
+        self.addLink(s20, s21)
+        self.addLink(s20, s22)
+        self.addLink(s20, s23)
+        self.addLink(s20, s24)
+        self.addLink(s20, s25)
+        self.addLink(s21, s22)
+        self.addLink(s21, s23)
+        self.addLink(s21, s24)
+        self.addLink(s21, s25)
+        self.addLink(s22, s23)
+        self.addLink(s22, s24)
+        self.addLink(s22, s25)
+        self.addLink(s23, s24)
+        self.addLink(s23, s25)
+        self.addLink(s24, s25)
+
+topos = { 'chordal': ( lambda: chordalTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = chordalTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch,autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordalIpv6.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordalIpv6.py
new file mode 100755
index 0000000..87d5946
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoChordalIpv6.py
@@ -0,0 +1,428 @@
+#!/usr/bin/python
+"""
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Host, RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class dualStackHost( Host ):
+    def config( self, v6Addr='1000::1/64', **params ):
+        r = super( Host, self ).config( **params )
+        intf = self.defaultIntf()
+        self.cmd( 'ip -6 addr add %s dev %s' % ( v6Addr, intf ) )
+        return r
+
+class chordalTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+        s4 = self.addSwitch( 's4' )
+        s5 = self.addSwitch( 's5' )
+        s6 = self.addSwitch( 's6' )
+        s7 = self.addSwitch( 's7' )
+        s8 = self.addSwitch( 's8' )
+        s9 = self.addSwitch( 's9' )
+        s10 = self.addSwitch( 's10' )
+        s11 = self.addSwitch( 's11' )
+        s12 = self.addSwitch( 's12' )
+        s13 = self.addSwitch( 's13' )
+        s14 = self.addSwitch( 's14' )
+        s15 = self.addSwitch( 's15' )
+        s16 = self.addSwitch( 's16' )
+        s17 = self.addSwitch( 's17' )
+        s18 = self.addSwitch( 's18' )
+        s19 = self.addSwitch( 's19' )
+        s20 = self.addSwitch( 's20' )
+        s21 = self.addSwitch( 's21' )
+        s22 = self.addSwitch( 's22' )
+        s23 = self.addSwitch( 's23' )
+        s24 = self.addSwitch( 's24' )
+        s25 = self.addSwitch( 's25' )
+
+        # ... and now hosts
+        s1_host = self.addHost( 'h1', ip='10.1.0.1/24', cls=dualStackHost, v6Addr='1000::1/64' )
+        s2_host = self.addHost( 'h2', ip='10.1.0.2/24', cls=dualStackHost, v6Addr='1000::2/64' )
+        s3_host = self.addHost( 'h3', ip='10.1.0.3/24', cls=dualStackHost, v6Addr='1000::3/64' )
+        s4_host = self.addHost( 'h4', ip='10.1.0.4/24', cls=dualStackHost, v6Addr='1000::4/64' )
+        s5_host = self.addHost( 'h5', ip='10.1.0.5/24', cls=dualStackHost, v6Addr='1000::5/64' )
+        s6_host = self.addHost( 'h6', ip='10.1.0.6/24', cls=dualStackHost, v6Addr='1000::6/64' )
+        s7_host = self.addHost( 'h7', ip='10.1.0.7/24', cls=dualStackHost, v6Addr='1000::7/64' )
+        s8_host = self.addHost( 'h8', ip='10.1.0.8/24', cls=dualStackHost, v6Addr='1000::8/64' )
+        s9_host = self.addHost( 'h9', ip='10.1.0.9/24', cls=dualStackHost, v6Addr='1000::9/64' )
+        s10_host = self.addHost( 'h10', ip='10.1.0.10/24', cls=dualStackHost, v6Addr='1000::10/64' )
+        s11_host = self.addHost( 'h11', ip='10.1.0.11/24', cls=dualStackHost, v6Addr='1000::11/64' )
+        s12_host = self.addHost( 'h12', ip='10.1.0.12/24', cls=dualStackHost, v6Addr='1000::12/64' )
+        s13_host = self.addHost( 'h13', ip='10.1.0.13/24', cls=dualStackHost, v6Addr='1000::13/64' )
+        s14_host = self.addHost( 'h14', ip='10.1.0.14/24', cls=dualStackHost, v6Addr='1000::14/64' )
+        s15_host = self.addHost( 'h15', ip='10.1.0.15/24', cls=dualStackHost, v6Addr='1000::15/64' )
+        s16_host = self.addHost( 'h16', ip='10.1.0.16/24', cls=dualStackHost, v6Addr='1000::16/64' )
+        s17_host = self.addHost( 'h17', ip='10.1.0.17/24', cls=dualStackHost, v6Addr='1000::17/64' )
+        s18_host = self.addHost( 'h18', ip='10.1.0.18/24', cls=dualStackHost, v6Addr='1000::18/64' )
+        s19_host = self.addHost( 'h19', ip='10.1.0.19/24', cls=dualStackHost, v6Addr='1000::19/64' )
+        s20_host = self.addHost( 'h20', ip='10.1.0.20/24', cls=dualStackHost, v6Addr='1000::20/64' )
+        s21_host = self.addHost( 'h21', ip='10.1.0.21/24', cls=dualStackHost, v6Addr='1000::21/64' )
+        s22_host = self.addHost( 'h22', ip='10.1.0.22/24', cls=dualStackHost, v6Addr='1000::22/64' )
+        s23_host = self.addHost( 'h23', ip='10.1.0.23/24', cls=dualStackHost, v6Addr='1000::23/64' )
+        s24_host = self.addHost( 'h24', ip='10.1.0.24/24', cls=dualStackHost, v6Addr='1000::24/64' )
+        s25_host = self.addHost( 'h25', ip='10.1.0.25/24', cls=dualStackHost, v6Addr='1000::25/64' )
+
+        # add edges between switch and corresponding host
+        self.addLink( s1 , s1_host )
+        self.addLink( s2 , s2_host )
+        self.addLink( s3 , s3_host )
+        self.addLink( s4 , s4_host )
+        self.addLink( s5 , s5_host )
+        self.addLink( s6 , s6_host )
+        self.addLink( s7 , s7_host )
+        self.addLink( s8 , s8_host )
+        self.addLink( s9 , s9_host )
+        self.addLink( s10 , s10_host )
+        self.addLink( s11 , s11_host )
+        self.addLink( s12 , s12_host )
+        self.addLink( s13 , s13_host )
+        self.addLink( s14 , s14_host )
+        self.addLink( s15 , s15_host )
+        self.addLink( s16 , s16_host )
+        self.addLink( s17 , s17_host )
+        self.addLink( s18 , s18_host )
+        self.addLink( s19 , s19_host )
+        self.addLink( s20 , s20_host )
+        self.addLink( s21 , s21_host )
+        self.addLink( s22 , s22_host )
+        self.addLink( s23 , s23_host )
+        self.addLink( s24 , s24_host )
+        self.addLink( s25 , s25_host )
+        self.addLink(s1, s2)
+        self.addLink(s1, s3)
+        self.addLink(s1, s4)
+        self.addLink(s1, s5)
+        self.addLink(s1, s6)
+        self.addLink(s1, s7)
+        self.addLink(s1, s8)
+        self.addLink(s1, s9)
+        self.addLink(s1, s10)
+        self.addLink(s1, s11)
+        self.addLink(s1, s12)
+        self.addLink(s1, s13)
+        self.addLink(s1, s14)
+        self.addLink(s1, s15)
+        self.addLink(s1, s16)
+        self.addLink(s1, s17)
+        self.addLink(s1, s18)
+        self.addLink(s1, s19)
+        self.addLink(s1, s20)
+        self.addLink(s1, s21)
+        self.addLink(s1, s22)
+        self.addLink(s1, s23)
+        self.addLink(s1, s24)
+        self.addLink(s1, s25)
+        self.addLink(s2, s3)
+        self.addLink(s2, s4)
+        self.addLink(s2, s5)
+        self.addLink(s2, s6)
+        self.addLink(s2, s7)
+        self.addLink(s2, s8)
+        self.addLink(s2, s9)
+        self.addLink(s2, s10)
+        self.addLink(s2, s11)
+        self.addLink(s2, s12)
+        self.addLink(s2, s13)
+        self.addLink(s2, s14)
+        self.addLink(s2, s15)
+        self.addLink(s2, s16)
+        self.addLink(s2, s17)
+        self.addLink(s2, s18)
+        self.addLink(s2, s19)
+        self.addLink(s2, s20)
+        self.addLink(s2, s21)
+        self.addLink(s2, s22)
+        self.addLink(s2, s23)
+        self.addLink(s2, s24)
+        self.addLink(s2, s25)
+        self.addLink(s3, s4)
+        self.addLink(s3, s5)
+        self.addLink(s3, s6)
+        self.addLink(s3, s7)
+        self.addLink(s3, s8)
+        self.addLink(s3, s9)
+        self.addLink(s3, s10)
+        self.addLink(s3, s11)
+        self.addLink(s3, s12)
+        self.addLink(s3, s13)
+        self.addLink(s3, s14)
+        self.addLink(s3, s15)
+        self.addLink(s3, s16)
+        self.addLink(s3, s17)
+        self.addLink(s3, s18)
+        self.addLink(s3, s19)
+        self.addLink(s3, s20)
+        self.addLink(s3, s21)
+        self.addLink(s3, s22)
+        self.addLink(s3, s23)
+        self.addLink(s3, s24)
+        self.addLink(s3, s25)
+        self.addLink(s4, s5)
+        self.addLink(s4, s6)
+        self.addLink(s4, s7)
+        self.addLink(s4, s8)
+        self.addLink(s4, s9)
+        self.addLink(s4, s10)
+        self.addLink(s4, s11)
+        self.addLink(s4, s12)
+        self.addLink(s4, s13)
+        self.addLink(s4, s14)
+        self.addLink(s4, s15)
+        self.addLink(s4, s16)
+        self.addLink(s4, s17)
+        self.addLink(s4, s18)
+        self.addLink(s4, s19)
+        self.addLink(s4, s20)
+        self.addLink(s4, s21)
+        self.addLink(s4, s22)
+        self.addLink(s4, s23)
+        self.addLink(s4, s24)
+        self.addLink(s4, s25)
+        self.addLink(s5, s6)
+        self.addLink(s5, s7)
+        self.addLink(s5, s8)
+        self.addLink(s5, s9)
+        self.addLink(s5, s10)
+        self.addLink(s5, s11)
+        self.addLink(s5, s12)
+        self.addLink(s5, s13)
+        self.addLink(s5, s14)
+        self.addLink(s5, s15)
+        self.addLink(s5, s16)
+        self.addLink(s5, s17)
+        self.addLink(s5, s18)
+        self.addLink(s5, s19)
+        self.addLink(s5, s20)
+        self.addLink(s5, s21)
+        self.addLink(s5, s22)
+        self.addLink(s5, s23)
+        self.addLink(s5, s24)
+        self.addLink(s5, s25)
+        self.addLink(s6, s7)
+        self.addLink(s6, s8)
+        self.addLink(s6, s9)
+        self.addLink(s6, s10)
+        self.addLink(s6, s11)
+        self.addLink(s6, s12)
+        self.addLink(s6, s13)
+        self.addLink(s6, s14)
+        self.addLink(s6, s15)
+        self.addLink(s6, s16)
+        self.addLink(s6, s17)
+        self.addLink(s6, s18)
+        self.addLink(s6, s19)
+        self.addLink(s6, s20)
+        self.addLink(s6, s21)
+        self.addLink(s6, s22)
+        self.addLink(s6, s23)
+        self.addLink(s6, s24)
+        self.addLink(s6, s25)
+        self.addLink(s7, s8)
+        self.addLink(s7, s9)
+        self.addLink(s7, s10)
+        self.addLink(s7, s11)
+        self.addLink(s7, s12)
+        self.addLink(s7, s13)
+        self.addLink(s7, s14)
+        self.addLink(s7, s15)
+        self.addLink(s7, s16)
+        self.addLink(s7, s17)
+        self.addLink(s7, s18)
+        self.addLink(s7, s19)
+        self.addLink(s7, s20)
+        self.addLink(s7, s21)
+        self.addLink(s7, s22)
+        self.addLink(s7, s23)
+        self.addLink(s7, s24)
+        self.addLink(s7, s25)
+        self.addLink(s8, s9)
+        self.addLink(s8, s10)
+        self.addLink(s8, s11)
+        self.addLink(s8, s12)
+        self.addLink(s8, s13)
+        self.addLink(s8, s14)
+        self.addLink(s8, s15)
+        self.addLink(s8, s16)
+        self.addLink(s8, s17)
+        self.addLink(s8, s18)
+        self.addLink(s8, s19)
+        self.addLink(s8, s20)
+        self.addLink(s8, s21)
+        self.addLink(s8, s22)
+        self.addLink(s8, s23)
+        self.addLink(s8, s24)
+        self.addLink(s8, s25)
+        self.addLink(s9, s10)
+        self.addLink(s9, s11)
+        self.addLink(s9, s12)
+        self.addLink(s9, s13)
+        self.addLink(s9, s14)
+        self.addLink(s9, s15)
+        self.addLink(s9, s16)
+        self.addLink(s9, s17)
+        self.addLink(s9, s18)
+        self.addLink(s9, s19)
+        self.addLink(s9, s20)
+        self.addLink(s9, s21)
+        self.addLink(s9, s22)
+        self.addLink(s9, s23)
+        self.addLink(s9, s24)
+        self.addLink(s9, s25)
+        self.addLink(s10, s11)
+        self.addLink(s10, s12)
+        self.addLink(s10, s13)
+        self.addLink(s10, s14)
+        self.addLink(s10, s15)
+        self.addLink(s10, s16)
+        self.addLink(s10, s17)
+        self.addLink(s10, s18)
+        self.addLink(s10, s19)
+        self.addLink(s10, s20)
+        self.addLink(s10, s21)
+        self.addLink(s10, s22)
+        self.addLink(s10, s23)
+        self.addLink(s10, s24)
+        self.addLink(s10, s25)
+        self.addLink(s11, s12)
+        self.addLink(s11, s13)
+        self.addLink(s11, s14)
+        self.addLink(s11, s15)
+        self.addLink(s11, s16)
+        self.addLink(s11, s17)
+        self.addLink(s11, s18)
+        self.addLink(s11, s19)
+        self.addLink(s11, s20)
+        self.addLink(s11, s21)
+        self.addLink(s11, s22)
+        self.addLink(s11, s23)
+        self.addLink(s11, s24)
+        self.addLink(s11, s25)
+        self.addLink(s12, s13)
+        self.addLink(s12, s14)
+        self.addLink(s12, s15)
+        self.addLink(s12, s16)
+        self.addLink(s12, s17)
+        self.addLink(s12, s18)
+        self.addLink(s12, s19)
+        self.addLink(s12, s20)
+        self.addLink(s12, s21)
+        self.addLink(s12, s22)
+        self.addLink(s12, s23)
+        self.addLink(s12, s24)
+        self.addLink(s12, s25)
+        self.addLink(s13, s14)
+        self.addLink(s13, s15)
+        self.addLink(s13, s16)
+        self.addLink(s13, s17)
+        self.addLink(s13, s18)
+        self.addLink(s13, s19)
+        self.addLink(s13, s20)
+        self.addLink(s13, s21)
+        self.addLink(s13, s22)
+        self.addLink(s13, s23)
+        self.addLink(s13, s24)
+        self.addLink(s13, s25)
+        self.addLink(s14, s15)
+        self.addLink(s14, s16)
+        self.addLink(s14, s17)
+        self.addLink(s14, s18)
+        self.addLink(s14, s19)
+        self.addLink(s14, s20)
+        self.addLink(s14, s21)
+        self.addLink(s14, s22)
+        self.addLink(s14, s23)
+        self.addLink(s14, s24)
+        self.addLink(s14, s25)
+        self.addLink(s15, s16)
+        self.addLink(s15, s17)
+        self.addLink(s15, s18)
+        self.addLink(s15, s19)
+        self.addLink(s15, s20)
+        self.addLink(s15, s21)
+        self.addLink(s15, s22)
+        self.addLink(s15, s23)
+        self.addLink(s15, s24)
+        self.addLink(s15, s25)
+        self.addLink(s16, s17)
+        self.addLink(s16, s18)
+        self.addLink(s16, s19)
+        self.addLink(s16, s20)
+        self.addLink(s16, s21)
+        self.addLink(s16, s22)
+        self.addLink(s16, s23)
+        self.addLink(s16, s24)
+        self.addLink(s16, s25)
+        self.addLink(s17, s18)
+        self.addLink(s17, s19)
+        self.addLink(s17, s20)
+        self.addLink(s17, s21)
+        self.addLink(s17, s22)
+        self.addLink(s17, s23)
+        self.addLink(s17, s24)
+        self.addLink(s17, s25)
+        self.addLink(s18, s19)
+        self.addLink(s18, s20)
+        self.addLink(s18, s21)
+        self.addLink(s18, s22)
+        self.addLink(s18, s23)
+        self.addLink(s18, s24)
+        self.addLink(s18, s25)
+        self.addLink(s19, s20)
+        self.addLink(s19, s21)
+        self.addLink(s19, s22)
+        self.addLink(s19, s23)
+        self.addLink(s19, s24)
+        self.addLink(s19, s25)
+        self.addLink(s20, s21)
+        self.addLink(s20, s22)
+        self.addLink(s20, s23)
+        self.addLink(s20, s24)
+        self.addLink(s20, s25)
+        self.addLink(s21, s22)
+        self.addLink(s21, s23)
+        self.addLink(s21, s24)
+        self.addLink(s21, s25)
+        self.addLink(s22, s23)
+        self.addLink(s22, s24)
+        self.addLink(s22, s25)
+        self.addLink(s23, s24)
+        self.addLink(s23, s25)
+        self.addLink(s24, s25)
+
+topos = { 'chordal': ( lambda: chordalTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = chordalTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch,autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoRingIpv6.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoRingIpv6.py
new file mode 100755
index 0000000..4b81223
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoRingIpv6.py
@@ -0,0 +1,93 @@
+#!/usr/bin/python
+"""
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Host, RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class dualStackHost( Host ):
+    def config( self, v6Addr='1000::1/64', **params ):
+        r = super( Host, self ).config( **params )
+        intf = self.defaultIntf()
+        self.cmd( 'ip -6 addr add %s dev %s' % ( v6Addr, intf ) )
+        return r
+
+class ringTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+        s4 = self.addSwitch( 's4' )
+        s5 = self.addSwitch( 's5' )
+        s6 = self.addSwitch( 's6' )
+        s7 = self.addSwitch( 's7' )
+        s8 = self.addSwitch( 's8' )
+        s9 = self.addSwitch( 's9' )
+        s10 = self.addSwitch( 's10' )
+
+        # ... and now hosts
+        s1_host = self.addHost( 'h1', ip='10.1.0.1/24', cls=dualStackHost, v6Addr='1000::1/64' )
+        s2_host = self.addHost( 'h2', ip='10.1.0.2/24', cls=dualStackHost, v6Addr='1000::2/64' )
+        s3_host = self.addHost( 'h3', ip='10.1.0.3/24', cls=dualStackHost, v6Addr='1000::3/64' )
+        s4_host = self.addHost( 'h4', ip='10.1.0.4/24', cls=dualStackHost, v6Addr='1000::4/64' )
+        s5_host = self.addHost( 'h5', ip='10.1.0.5/24', cls=dualStackHost, v6Addr='1000::5/64' )
+        s6_host = self.addHost( 'h6', ip='10.1.0.6/24', cls=dualStackHost, v6Addr='1000::6/64' )
+        s7_host = self.addHost( 'h7', ip='10.1.0.7/24', cls=dualStackHost, v6Addr='1000::7/64' )
+        s8_host = self.addHost( 'h8', ip='10.1.0.8/24', cls=dualStackHost, v6Addr='1000::8/64' )
+        s9_host = self.addHost( 'h9', ip='10.1.0.9/24', cls=dualStackHost, v6Addr='1000::9/64' )
+        s10_host = self.addHost( 'h10', ip='10.1.0.10/24', cls=dualStackHost, v6Addr='1000::10/64' )
+
+        # add edges between switch and corresponding host
+        self.addLink( s1 , s1_host )
+        self.addLink( s2 , s2_host )
+        self.addLink( s3 , s3_host )
+        self.addLink( s4 , s4_host )
+        self.addLink( s5 , s5_host )
+        self.addLink( s6 , s6_host )
+        self.addLink( s7 , s7_host )
+        self.addLink( s8 , s8_host )
+        self.addLink( s9 , s9_host )
+        self.addLink( s10 , s10_host )
+        self.addLink(s1, s2)
+        self.addLink(s2, s3)
+        self.addLink(s3, s4)
+        self.addLink(s4, s5)
+        self.addLink(s5, s6)
+        self.addLink(s6, s7)
+        self.addLink(s7, s8)
+        self.addLink(s8, s9)
+        self.addLink(s9, s10)
+        self.addLink(s10, s1)
+
+topos = { 'ring': ( lambda: ringTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = ringTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch, autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpine.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpine.py
new file mode 100755
index 0000000..5787d93
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpine.py
@@ -0,0 +1,434 @@
+#!/usr/bin/python
+
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Controller, RemoteController, OVSController
+from mininet.node import CPULimitedHost, Host, Node
+from mininet.cli import CLI
+from mininet.log import setLogLevel, info
+from mininet.link import TCLink, Intf
+from subprocess import call
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class spineTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        # add nodes, Leaf switches
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+        s4 = self.addSwitch( 's4' )
+        s5 = self.addSwitch( 's5' )
+        s6 = self.addSwitch( 's6' )
+        s7 = self.addSwitch( 's7' )
+        s8 = self.addSwitch( 's8' )
+        s9 = self.addSwitch( 's9' )
+        s10 = self.addSwitch( 's10' )
+        s11 = self.addSwitch( 's11' )
+        s12 = self.addSwitch( 's12' )
+        s13 = self.addSwitch( 's13' )
+        s14 = self.addSwitch( 's14' )
+
+        # add nodes, Spine switches first...
+        s15 = self.addSwitch( 's15' )
+        s16 = self.addSwitch( 's16' )
+        s17 = self.addSwitch( 's17' )
+        s18 = self.addSwitch( 's18' )
+        s19 = self.addSwitch( 's19' )
+        s20 = self.addSwitch( 's20' )
+        s21 = self.addSwitch( 's21' )
+        s22 = self.addSwitch( 's22' )
+        s23 = self.addSwitch( 's23' )
+        s24 = self.addSwitch( 's24' )
+        s25 = self.addSwitch( 's25' )
+        s26 = self.addSwitch( 's26' )
+        s27 = self.addSwitch( 's27' )
+        s28 = self.addSwitch( 's28' )
+        s29 = self.addSwitch( 's29' )
+        s30 = self.addSwitch( 's30' )
+        s31 = self.addSwitch( 's31' )
+        s32 = self.addSwitch( 's32' )
+        s33 = self.addSwitch( 's33' )
+        s34 = self.addSwitch( 's34' )
+        s35 = self.addSwitch( 's35' )
+        s36 = self.addSwitch( 's36' )
+        s37 = self.addSwitch( 's37' )
+        s38 = self.addSwitch( 's38' )
+        s39 = self.addSwitch( 's39' )
+        s40 = self.addSwitch( 's40' )
+        s41 = self.addSwitch( 's41' )
+        s42 = self.addSwitch( 's42' )
+        s43 = self.addSwitch( 's43' )
+        s44 = self.addSwitch( 's44' )
+        s45 = self.addSwitch( 's45' )
+        s46 = self.addSwitch( 's46' )
+        s47 = self.addSwitch( 's47' )
+        s48 = self.addSwitch( 's48' )
+        s49 = self.addSwitch( 's49' )
+        s50 = self.addSwitch( 's50' )
+        s51 = self.addSwitch( 's51' )
+        s52 = self.addSwitch( 's52' )
+        s53 = self.addSwitch( 's53' )
+        s54 = self.addSwitch( 's54' )
+        s55 = self.addSwitch( 's55' )
+        s56 = self.addSwitch( 's56' )
+        s57 = self.addSwitch( 's57' )
+        s58 = self.addSwitch( 's58' )
+        s59 = self.addSwitch( 's59' )
+        s60 = self.addSwitch( 's60' )
+        s61 = self.addSwitch( 's61' )
+        s62 = self.addSwitch( 's62' )
+        s63 = self.addSwitch( 's63' )
+        s64 = self.addSwitch( 's64' )
+        s65 = self.addSwitch( 's65' )
+        s66 = self.addSwitch( 's66' )
+        s67 = self.addSwitch( 's67' )
+        s68 = self.addSwitch( 's68' )
+        s69 = self.addSwitch( 's69' )
+        s70 = self.addSwitch( 's70' )
+        s71 = self.addSwitch( 's71' )
+        s72 = self.addSwitch( 's72' )
+        s73 = self.addSwitch( 's73' )
+        s74 = self.addSwitch( 's74' )
+        s75 = self.addSwitch( 's75' )
+        s76 = self.addSwitch( 's76' )
+        s77 = self.addSwitch( 's77' )
+        s78 = self.addSwitch( 's78' )
+
+        # ... and now hosts
+        #s1_host = self.addHost( 'h1' )
+        #s2_host = self.addHost( 'h2' )
+        #s3_host = self.addHost( 'h3' )
+        #s4_host = self.addHost( 'h4' )
+        #s5_host = self.addHost( 'h5' )
+        #s6_host = self.addHost( 'h6' )
+        #s7_host = self.addHost( 'h7' )
+        #s8_host = self.addHost( 'h8' )
+        #s9_host = self.addHost( 'h9' )
+        #s10_host = self.addHost( 'h10' )
+        s11_host = self.addHost( 'h11' )
+        s12_host = self.addHost( 'h12' )
+        s13_host = self.addHost( 'h13' )
+        s14_host = self.addHost( 'h14' )
+        s15_host = self.addHost( 'h15' )
+        s16_host = self.addHost( 'h16' )
+        s17_host = self.addHost( 'h17' )
+        s18_host = self.addHost( 'h18' )
+        s19_host = self.addHost( 'h19' )
+        s20_host = self.addHost( 'h20' )
+        s21_host = self.addHost( 'h21' )
+        s22_host = self.addHost( 'h22' )
+        s23_host = self.addHost( 'h23' )
+        s24_host = self.addHost( 'h24' )
+        s25_host = self.addHost( 'h25' )
+        s26_host = self.addHost( 'h26' )
+        s27_host = self.addHost( 'h27' )
+        s28_host = self.addHost( 'h28' )
+        s29_host = self.addHost( 'h29' )
+        s30_host = self.addHost( 'h30' )
+        s31_host = self.addHost( 'h31' )
+        s32_host = self.addHost( 'h32' )
+        s33_host = self.addHost( 'h33' )
+        s34_host = self.addHost( 'h34' )
+        s35_host = self.addHost( 'h35' )
+        s36_host = self.addHost( 'h36' )
+        s37_host = self.addHost( 'h37' )
+        s38_host = self.addHost( 'h38' )
+        s39_host = self.addHost( 'h39' )
+        s40_host = self.addHost( 'h40' )
+        s41_host = self.addHost( 'h41' )
+        s42_host = self.addHost( 'h42' )
+        s43_host = self.addHost( 'h43' )
+        s44_host = self.addHost( 'h44' )
+        s45_host = self.addHost( 'h45' )
+        s46_host = self.addHost( 'h46' )
+        s47_host = self.addHost( 'h47' )
+        s48_host = self.addHost( 'h48' )
+        s49_host = self.addHost( 'h49' )
+        s50_host = self.addHost( 'h50' )
+        s51_host = self.addHost( 'h51' )
+        s52_host = self.addHost( 'h52' )
+        s53_host = self.addHost( 'h53' )
+        s54_host = self.addHost( 'h54' )
+        s55_host = self.addHost( 'h55' )
+        s56_host = self.addHost( 'h56' )
+        s57_host = self.addHost( 'h57' )
+        s58_host = self.addHost( 'h58' )
+        s59_host = self.addHost( 'h59' )
+        s60_host = self.addHost( 'h60' )
+        s61_host = self.addHost( 'h61' )
+        s62_host = self.addHost( 'h62' )
+        s63_host = self.addHost( 'h63' )
+        s64_host = self.addHost( 'h64' )
+        s65_host = self.addHost( 'h65' )
+        s66_host = self.addHost( 'h66' )
+        s67_host = self.addHost( 'h67' )
+        s68_host = self.addHost( 'h68' )
+        s69_host = self.addHost( 'h69' )
+        s70_host = self.addHost( 'h70' )
+        s71_host = self.addHost( 'h71' )
+        s72_host = self.addHost( 'h72' )
+        s73_host = self.addHost( 'h73' )
+        s74_host = self.addHost( 'h74' )
+        s75_host = self.addHost( 'h75' )
+        s76_host = self.addHost( 'h76' )
+        s77_host = self.addHost( 'h77' )
+        s78_host = self.addHost( 'h78' )
+
+        # add edges between switch and corresponding host
+        #self.addLink( s1 , s1_host )
+        #self.addLink( s2 , s2_host )
+        #self.addLink( s3 , s3_host )
+        #self.addLink( s4 , s4_host )
+        #self.addLink( s5 , s5_host )
+        #self.addLink( s6 , s6_host )
+        #self.addLink( s7 , s7_host )
+        #self.addLink( s8 , s8_host )
+        #self.addLink( s9 , s9_host )
+        #self.addLink( s10 , s10_host )
+        self.addLink( s11 , s11_host )
+        self.addLink( s12 , s12_host )
+        self.addLink( s13 , s13_host )
+        self.addLink( s14 , s14_host )
+        self.addLink( s15 , s15_host )
+        self.addLink( s16 , s16_host )
+        self.addLink( s17 , s17_host )
+        self.addLink( s18 , s18_host )
+        self.addLink( s19 , s19_host )
+        self.addLink( s20 , s20_host )
+        self.addLink( s21 , s21_host )
+        self.addLink( s22 , s22_host )
+        self.addLink( s23 , s23_host )
+        self.addLink( s24 , s24_host )
+        self.addLink( s25 , s25_host )
+        self.addLink( s26 , s26_host )
+        self.addLink( s27 , s27_host )
+        self.addLink( s28 , s28_host )
+        self.addLink( s29 , s29_host )
+        self.addLink( s30 , s30_host )
+        self.addLink( s31 , s31_host )
+        self.addLink( s32 , s32_host )
+        self.addLink( s33 , s33_host )
+        self.addLink( s34 , s34_host )
+        self.addLink( s35 , s35_host )
+        self.addLink( s36 , s36_host )
+        self.addLink( s37 , s37_host )
+        self.addLink( s38 , s38_host )
+        self.addLink( s39 , s39_host )
+        self.addLink( s40 , s40_host )
+        self.addLink( s41 , s41_host )
+        self.addLink( s42 , s42_host )
+        self.addLink( s43 , s43_host )
+        self.addLink( s44 , s44_host )
+        self.addLink( s45 , s45_host )
+        self.addLink( s46 , s46_host )
+        self.addLink( s47 , s47_host )
+        self.addLink( s48 , s48_host )
+        self.addLink( s49 , s49_host )
+        self.addLink( s50 , s50_host )
+        self.addLink( s51 , s51_host )
+        self.addLink( s52 , s52_host )
+        self.addLink( s53 , s53_host )
+        self.addLink( s54 , s54_host )
+        self.addLink( s55 , s55_host )
+        self.addLink( s56 , s56_host )
+        self.addLink( s57 , s57_host )
+        self.addLink( s58 , s58_host )
+        self.addLink( s59 , s59_host )
+        self.addLink( s60 , s60_host )
+        self.addLink( s61 , s61_host )
+        self.addLink( s62 , s62_host )
+        self.addLink( s63 , s63_host )
+        self.addLink( s64 , s64_host )
+        self.addLink( s65 , s65_host )
+        self.addLink( s66 , s66_host )
+        self.addLink( s67 , s67_host )
+        self.addLink( s68 , s68_host )
+        self.addLink( s69 , s69_host )
+        self.addLink( s70 , s70_host )
+        self.addLink( s71 , s71_host )
+        self.addLink( s72 , s72_host )
+        self.addLink( s73 , s73_host )
+        self.addLink( s74 , s74_host )
+        self.addLink( s75 , s75_host )
+        self.addLink( s76 , s76_host )
+        self.addLink( s77 , s77_host )
+        self.addLink( s78 , s78_host )
+
+        #info( '*** Add Leaf links\n')
+        self.addLink(s1, s9)
+        self.addLink(s2, s10)
+        self.addLink(s3, s9)
+        self.addLink(s4, s10)
+        self.addLink(s5, s9)
+        self.addLink(s6, s10)
+        self.addLink(s7, s9)
+        self.addLink(s8, s10)
+        self.addLink(s9, s11)
+        self.addLink(s9, s12)
+        self.addLink(s10, s13)
+        self.addLink(s10, s14)
+        self.addLink(s11, s12)
+        self.addLink(s13, s14)
+
+        #info( '*** Add Spine-1 links\n')
+        self.addLink(s15, s1)
+        self.addLink(s15, s2)
+        self.addLink(s16, s1)
+        self.addLink(s16, s2)
+        self.addLink(s17, s1)
+        self.addLink(s17, s2)
+        self.addLink(s18, s1)
+        self.addLink(s18, s2)
+        self.addLink(s19, s1)
+        self.addLink(s19, s2)
+        self.addLink(s20, s1)
+        self.addLink(s20, s2)
+        self.addLink(s21, s1)
+        self.addLink(s21, s2)
+        self.addLink(s22, s1)
+        self.addLink(s22, s2)
+        self.addLink(s23, s1)
+        self.addLink(s23, s2)
+        self.addLink(s24, s1)
+        self.addLink(s24, s2)
+        self.addLink(s25, s1)
+        self.addLink(s25, s2)
+        self.addLink(s26, s1)
+        self.addLink(s26, s2)
+        self.addLink(s27, s1)
+        self.addLink(s27, s2)
+        self.addLink(s28, s1)
+        self.addLink(s28, s2)
+        self.addLink(s29, s1)
+        self.addLink(s29, s2)
+        self.addLink(s30, s1)
+        self.addLink(s30, s2)
+
+        #info( '*** Add Spine-2 links\n')
+        self.addLink(s31, s3)
+        self.addLink(s31, s4)
+        self.addLink(s32, s3)
+        self.addLink(s32, s4)
+        self.addLink(s33, s3)
+        self.addLink(s33, s4)
+        self.addLink(s34, s3)
+        self.addLink(s34, s4)
+        self.addLink(s35, s3)
+        self.addLink(s35, s4)
+        self.addLink(s36, s3)
+        self.addLink(s36, s4)
+        self.addLink(s37, s3)
+        self.addLink(s37, s4)
+        self.addLink(s38, s3)
+        self.addLink(s38, s4)
+        self.addLink(s39, s3)
+        self.addLink(s39, s4)
+        self.addLink(s40, s3)
+        self.addLink(s40, s4)
+        self.addLink(s41, s3)
+        self.addLink(s41, s4)
+        self.addLink(s42, s3)
+        self.addLink(s42, s4)
+        self.addLink(s43, s3)
+        self.addLink(s43, s4)
+        self.addLink(s44, s3)
+        self.addLink(s44, s4)
+        self.addLink(s45, s3)
+        self.addLink(s45, s4)
+        self.addLink(s46, s3)
+        self.addLink(s46, s4)
+
+        #info( '*** Add Spine-3 links\n')
+        self.addLink(s47, s5)
+        self.addLink(s47, s6)
+        self.addLink(s48, s5)
+        self.addLink(s48, s6)
+        self.addLink(s49, s5)
+        self.addLink(s49, s6)
+        self.addLink(s50, s5)
+        self.addLink(s50, s6)
+        self.addLink(s51, s5)
+        self.addLink(s51, s6)
+        self.addLink(s52, s5)
+        self.addLink(s52, s6)
+        self.addLink(s53, s5)
+        self.addLink(s53, s6)
+        self.addLink(s54, s5)
+        self.addLink(s54, s6)
+        self.addLink(s55, s5)
+        self.addLink(s55, s6)
+        self.addLink(s56, s5)
+        self.addLink(s56, s6)
+        self.addLink(s57, s5)
+        self.addLink(s57, s6)
+        self.addLink(s58, s5)
+        self.addLink(s58, s6)
+        self.addLink(s59, s5)
+        self.addLink(s59, s6)
+        self.addLink(s60, s5)
+        self.addLink(s60, s6)
+        self.addLink(s61, s5)
+        self.addLink(s61, s6)
+        self.addLink(s62, s5)
+        self.addLink(s62, s6)
+
+        #info( '*** Add Spine-4 links\n')
+        self.addLink(s63, s7)
+        self.addLink(s63, s8)
+        self.addLink(s64, s7)
+        self.addLink(s64, s8)
+        self.addLink(s65, s7)
+        self.addLink(s65, s8)
+        self.addLink(s66, s7)
+        self.addLink(s66, s8)
+        self.addLink(s67, s7)
+        self.addLink(s67, s8)
+        self.addLink(s68, s7)
+        self.addLink(s68, s8)
+        self.addLink(s69, s7)
+        self.addLink(s69, s8)
+        self.addLink(s70, s7)
+        self.addLink(s70, s8)
+        self.addLink(s71, s7)
+        self.addLink(s71, s8)
+        self.addLink(s72, s7)
+        self.addLink(s72, s8)
+        self.addLink(s73, s7)
+        self.addLink(s73, s8)
+        self.addLink(s74, s7)
+        self.addLink(s74, s8)
+        self.addLink(s75, s7)
+        self.addLink(s75, s8)
+        self.addLink(s76, s7)
+        self.addLink(s76, s8)
+        self.addLink(s77, s7)
+        self.addLink(s77, s8)
+        self.addLink(s78, s7)
+        self.addLink(s78, s8)
+
+topos = { 'spine': ( lambda: spineTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = spineTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch, link=TCLink, autoSetMacs = True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpineIpv6.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpineIpv6.py
new file mode 100755
index 0000000..3f35494
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoSpineIpv6.py
@@ -0,0 +1,441 @@
+#!/usr/bin/python
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Host, RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class dualStackHost( Host ):
+    def config( self, v6Addr='1000::1/64', **params ):
+        r = super( Host, self ).config( **params )
+        intf = self.defaultIntf()
+        self.cmd( 'ip -6 addr add %s dev %s' % ( v6Addr, intf ) )
+        return r
+
+class spineTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        # add nodes, Leaf switches
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+        s4 = self.addSwitch( 's4' )
+        s5 = self.addSwitch( 's5' )
+        s6 = self.addSwitch( 's6' )
+        s7 = self.addSwitch( 's7' )
+        s8 = self.addSwitch( 's8' )
+        s9 = self.addSwitch( 's9' )
+        s10 = self.addSwitch( 's10' )
+        s11 = self.addSwitch( 's11' )
+        s12 = self.addSwitch( 's12' )
+        s13 = self.addSwitch( 's13' )
+        s14 = self.addSwitch( 's14' )
+
+        # add nodes, Spine switches first...
+        s15 = self.addSwitch( 's15' )
+        s16 = self.addSwitch( 's16' )
+        s17 = self.addSwitch( 's17' )
+        s18 = self.addSwitch( 's18' )
+        s19 = self.addSwitch( 's19' )
+        s20 = self.addSwitch( 's20' )
+        s21 = self.addSwitch( 's21' )
+        s22 = self.addSwitch( 's22' )
+        s23 = self.addSwitch( 's23' )
+        s24 = self.addSwitch( 's24' )
+        s25 = self.addSwitch( 's25' )
+        s26 = self.addSwitch( 's26' )
+        s27 = self.addSwitch( 's27' )
+        s28 = self.addSwitch( 's28' )
+        s29 = self.addSwitch( 's29' )
+        s30 = self.addSwitch( 's30' )
+        s31 = self.addSwitch( 's31' )
+        s32 = self.addSwitch( 's32' )
+        s33 = self.addSwitch( 's33' )
+        s34 = self.addSwitch( 's34' )
+        s35 = self.addSwitch( 's35' )
+        s36 = self.addSwitch( 's36' )
+        s37 = self.addSwitch( 's37' )
+        s38 = self.addSwitch( 's38' )
+        s39 = self.addSwitch( 's39' )
+        s40 = self.addSwitch( 's40' )
+        s41 = self.addSwitch( 's41' )
+        s42 = self.addSwitch( 's42' )
+        s43 = self.addSwitch( 's43' )
+        s44 = self.addSwitch( 's44' )
+        s45 = self.addSwitch( 's45' )
+        s46 = self.addSwitch( 's46' )
+        s47 = self.addSwitch( 's47' )
+        s48 = self.addSwitch( 's48' )
+        s49 = self.addSwitch( 's49' )
+        s50 = self.addSwitch( 's50' )
+        s51 = self.addSwitch( 's51' )
+        s52 = self.addSwitch( 's52' )
+        s53 = self.addSwitch( 's53' )
+        s54 = self.addSwitch( 's54' )
+        s55 = self.addSwitch( 's55' )
+        s56 = self.addSwitch( 's56' )
+        s57 = self.addSwitch( 's57' )
+        s58 = self.addSwitch( 's58' )
+        s59 = self.addSwitch( 's59' )
+        s60 = self.addSwitch( 's60' )
+        s61 = self.addSwitch( 's61' )
+        s62 = self.addSwitch( 's62' )
+        s63 = self.addSwitch( 's63' )
+        s64 = self.addSwitch( 's64' )
+        s65 = self.addSwitch( 's65' )
+        s66 = self.addSwitch( 's66' )
+        s67 = self.addSwitch( 's67' )
+        s68 = self.addSwitch( 's68' )
+        s69 = self.addSwitch( 's69' )
+        s70 = self.addSwitch( 's70' )
+        s71 = self.addSwitch( 's71' )
+        s72 = self.addSwitch( 's72' )
+        s73 = self.addSwitch( 's73' )
+        s74 = self.addSwitch( 's74' )
+        s75 = self.addSwitch( 's75' )
+        s76 = self.addSwitch( 's76' )
+        s77 = self.addSwitch( 's77' )
+        s78 = self.addSwitch( 's78' )
+
+
+        # ... and now hosts
+        # s1_host = self.addHost( 'h1', ip='10.1.0.1/24', cls=dualStackHost, v6Addr='1000::1/64' )
+        # s2_host = self.addHost( 'h2', ip='10.1.0.2/24', cls=dualStackHost, v6Addr='1000::2/64' )
+        # s3_host = self.addHost( 'h3', ip='10.1.0.3/24', cls=dualStackHost, v6Addr='1000::3/64' )
+        # s4_host = self.addHost( 'h4', ip='10.1.0.4/24', cls=dualStackHost, v6Addr='1000::4/64' )
+        # s5_host = self.addHost( 'h5', ip='10.1.0.5/24', cls=dualStackHost, v6Addr='1000::5/64' )
+        # s6_host = self.addHost( 'h6', ip='10.1.0.6/24', cls=dualStackHost, v6Addr='1000::6/64' )
+        # s7_host = self.addHost( 'h7', ip='10.1.0.7/24', cls=dualStackHost, v6Addr='1000::7/64' )
+        # s8_host = self.addHost( 'h8', ip='10.1.0.8/24', cls=dualStackHost, v6Addr='1000::8/64' )
+        # s9_host = self.addHost( 'h9', ip='10.1.0.9/24', cls=dualStackHost, v6Addr='1000::9/64' )
+        # s10_host = self.addHost( 'h10', ip='10.1.0.10/24', cls=dualStackHost, v6Addr='1000::10/64' )
+        s11_host = self.addHost( 'h11', ip='10.1.0.11/24', cls=dualStackHost, v6Addr='1000::11/64' )
+        s12_host = self.addHost( 'h12', ip='10.1.0.12/24', cls=dualStackHost, v6Addr='1000::12/64' )
+        s13_host = self.addHost( 'h13', ip='10.1.0.13/24', cls=dualStackHost, v6Addr='1000::13/64' )
+        s14_host = self.addHost( 'h14', ip='10.1.0.14/24', cls=dualStackHost, v6Addr='1000::14/64' )
+        s15_host = self.addHost( 'h15', ip='10.1.0.15/24', cls=dualStackHost, v6Addr='1000::15/64' )
+        s16_host = self.addHost( 'h16', ip='10.1.0.16/24', cls=dualStackHost, v6Addr='1000::16/64' )
+        s17_host = self.addHost( 'h17', ip='10.1.0.17/24', cls=dualStackHost, v6Addr='1000::17/64' )
+        s18_host = self.addHost( 'h18', ip='10.1.0.18/24', cls=dualStackHost, v6Addr='1000::18/64' )
+        s19_host = self.addHost( 'h19', ip='10.1.0.19/24', cls=dualStackHost, v6Addr='1000::19/64' )
+        s20_host = self.addHost( 'h20', ip='10.1.0.20/24', cls=dualStackHost, v6Addr='1000::20/64' )
+        s21_host = self.addHost( 'h21', ip='10.1.0.21/24', cls=dualStackHost, v6Addr='1000::21/64' )
+        s22_host = self.addHost( 'h22', ip='10.1.0.22/24', cls=dualStackHost, v6Addr='1000::22/64' )
+        s23_host = self.addHost( 'h23', ip='10.1.0.23/24', cls=dualStackHost, v6Addr='1000::23/64' )
+        s24_host = self.addHost( 'h24', ip='10.1.0.24/24', cls=dualStackHost, v6Addr='1000::24/64' )
+        s25_host = self.addHost( 'h25', ip='10.1.0.25/24', cls=dualStackHost, v6Addr='1000::25/64' )
+        s26_host = self.addHost( 'h26', ip='10.1.0.26/24', cls=dualStackHost, v6Addr='1000::26/64' )
+        s27_host = self.addHost( 'h27', ip='10.1.0.27/24', cls=dualStackHost, v6Addr='1000::27/64' )
+        s28_host = self.addHost( 'h28', ip='10.1.0.28/24', cls=dualStackHost, v6Addr='1000::28/64' )
+        s29_host = self.addHost( 'h29', ip='10.1.0.29/24', cls=dualStackHost, v6Addr='1000::29/64' )
+        s30_host = self.addHost( 'h30', ip='10.1.0.30/24', cls=dualStackHost, v6Addr='1000::30/64' )
+        s31_host = self.addHost( 'h31', ip='10.1.0.31/24', cls=dualStackHost, v6Addr='1000::31/64' )
+        s32_host = self.addHost( 'h32', ip='10.1.0.32/24', cls=dualStackHost, v6Addr='1000::32/64' )
+        s33_host = self.addHost( 'h33', ip='10.1.0.33/24', cls=dualStackHost, v6Addr='1000::33/64' )
+        s34_host = self.addHost( 'h34', ip='10.1.0.34/24', cls=dualStackHost, v6Addr='1000::34/64' )
+        s35_host = self.addHost( 'h35', ip='10.1.0.35/24', cls=dualStackHost, v6Addr='1000::35/64' )
+        s36_host = self.addHost( 'h36', ip='10.1.0.36/24', cls=dualStackHost, v6Addr='1000::36/64' )
+        s37_host = self.addHost( 'h37', ip='10.1.0.37/24', cls=dualStackHost, v6Addr='1000::37/64' )
+        s38_host = self.addHost( 'h38', ip='10.1.0.38/24', cls=dualStackHost, v6Addr='1000::38/64' )
+        s39_host = self.addHost( 'h39', ip='10.1.0.39/24', cls=dualStackHost, v6Addr='1000::39/64' )
+        s40_host = self.addHost( 'h40', ip='10.1.0.40/24', cls=dualStackHost, v6Addr='1000::40/64' )
+        s41_host = self.addHost( 'h41', ip='10.1.0.41/24', cls=dualStackHost, v6Addr='1000::41/64' )
+        s42_host = self.addHost( 'h42', ip='10.1.0.42/24', cls=dualStackHost, v6Addr='1000::42/64' )
+        s43_host = self.addHost( 'h43', ip='10.1.0.43/24', cls=dualStackHost, v6Addr='1000::43/64' )
+        s44_host = self.addHost( 'h44', ip='10.1.0.44/24', cls=dualStackHost, v6Addr='1000::44/64' )
+        s45_host = self.addHost( 'h45', ip='10.1.0.45/24', cls=dualStackHost, v6Addr='1000::45/64' )
+        s46_host = self.addHost( 'h46', ip='10.1.0.46/24', cls=dualStackHost, v6Addr='1000::46/64' )
+        s47_host = self.addHost( 'h47', ip='10.1.0.47/24', cls=dualStackHost, v6Addr='1000::47/64' )
+        s48_host = self.addHost( 'h48', ip='10.1.0.48/24', cls=dualStackHost, v6Addr='1000::48/64' )
+        s49_host = self.addHost( 'h49', ip='10.1.0.49/24', cls=dualStackHost, v6Addr='1000::49/64' )
+        s50_host = self.addHost( 'h50', ip='10.1.0.50/24', cls=dualStackHost, v6Addr='1000::50/64' )
+        s51_host = self.addHost( 'h51', ip='10.1.0.51/24', cls=dualStackHost, v6Addr='1000::51/64' )
+        s52_host = self.addHost( 'h52', ip='10.1.0.52/24', cls=dualStackHost, v6Addr='1000::52/64' )
+        s53_host = self.addHost( 'h53', ip='10.1.0.53/24', cls=dualStackHost, v6Addr='1000::53/64' )
+        s54_host = self.addHost( 'h54', ip='10.1.0.54/24', cls=dualStackHost, v6Addr='1000::54/64' )
+        s55_host = self.addHost( 'h55', ip='10.1.0.55/24', cls=dualStackHost, v6Addr='1000::55/64' )
+        s56_host = self.addHost( 'h56', ip='10.1.0.56/24', cls=dualStackHost, v6Addr='1000::56/64' )
+        s57_host = self.addHost( 'h57', ip='10.1.0.57/24', cls=dualStackHost, v6Addr='1000::57/64' )
+        s58_host = self.addHost( 'h58', ip='10.1.0.58/24', cls=dualStackHost, v6Addr='1000::58/64' )
+        s59_host = self.addHost( 'h59', ip='10.1.0.59/24', cls=dualStackHost, v6Addr='1000::59/64' )
+        s60_host = self.addHost( 'h60', ip='10.1.0.60/24', cls=dualStackHost, v6Addr='1000::60/64' )
+        s61_host = self.addHost( 'h61', ip='10.1.0.61/24', cls=dualStackHost, v6Addr='1000::61/64' )
+        s62_host = self.addHost( 'h62', ip='10.1.0.62/24', cls=dualStackHost, v6Addr='1000::62/64' )
+        s63_host = self.addHost( 'h63', ip='10.1.0.63/24', cls=dualStackHost, v6Addr='1000::63/64' )
+        s64_host = self.addHost( 'h64', ip='10.1.0.64/24', cls=dualStackHost, v6Addr='1000::64/64' )
+        s65_host = self.addHost( 'h65', ip='10.1.0.65/24', cls=dualStackHost, v6Addr='1000::65/64' )
+        s66_host = self.addHost( 'h66', ip='10.1.0.66/24', cls=dualStackHost, v6Addr='1000::66/64' )
+        s67_host = self.addHost( 'h67', ip='10.1.0.67/24', cls=dualStackHost, v6Addr='1000::67/64' )
+        s68_host = self.addHost( 'h68', ip='10.1.0.68/24', cls=dualStackHost, v6Addr='1000::68/64' )
+        s69_host = self.addHost( 'h69', ip='10.1.0.69/24', cls=dualStackHost, v6Addr='1000::69/64' )
+        s70_host = self.addHost( 'h70', ip='10.1.0.70/24', cls=dualStackHost, v6Addr='1000::70/64' )
+        s71_host = self.addHost( 'h71', ip='10.1.0.71/24', cls=dualStackHost, v6Addr='1000::71/64' )
+        s72_host = self.addHost( 'h72', ip='10.1.0.72/24', cls=dualStackHost, v6Addr='1000::72/64' )
+        s73_host = self.addHost( 'h73', ip='10.1.0.73/24', cls=dualStackHost, v6Addr='1000::73/64' )
+        s74_host = self.addHost( 'h74', ip='10.1.0.74/24', cls=dualStackHost, v6Addr='1000::74/64' )
+        s75_host = self.addHost( 'h75', ip='10.1.0.75/24', cls=dualStackHost, v6Addr='1000::75/64' )
+        s76_host = self.addHost( 'h76', ip='10.1.0.76/24', cls=dualStackHost, v6Addr='1000::76/64' )
+        s77_host = self.addHost( 'h77', ip='10.1.0.77/24', cls=dualStackHost, v6Addr='1000::77/64' )
+        s78_host = self.addHost( 'h78', ip='10.1.0.78/24', cls=dualStackHost, v6Addr='1000::78/64' )
+
+        # add edges between switch and corresponding host
+        #self.addLink( s1 , s1_host )
+        #self.addLink( s2 , s2_host )
+        #self.addLink( s3 , s3_host )
+        #self.addLink( s4 , s4_host )
+        #self.addLink( s5 , s5_host )
+        #self.addLink( s6 , s6_host )
+        #self.addLink( s7 , s7_host )
+        #self.addLink( s8 , s8_host )
+        #self.addLink( s9 , s9_host )
+        #self.addLink( s10 , s10_host )
+        self.addLink( s11 , s11_host )
+        self.addLink( s12 , s12_host )
+        self.addLink( s13 , s13_host )
+        self.addLink( s14 , s14_host )
+        self.addLink( s15 , s15_host )
+        self.addLink( s16 , s16_host )
+        self.addLink( s17 , s17_host )
+        self.addLink( s18 , s18_host )
+        self.addLink( s19 , s19_host )
+        self.addLink( s20 , s20_host )
+        self.addLink( s21 , s21_host )
+        self.addLink( s22 , s22_host )
+        self.addLink( s23 , s23_host )
+        self.addLink( s24 , s24_host )
+        self.addLink( s25 , s25_host )
+        self.addLink( s26 , s26_host )
+        self.addLink( s27 , s27_host )
+        self.addLink( s28 , s28_host )
+        self.addLink( s29 , s29_host )
+        self.addLink( s30 , s30_host )
+        self.addLink( s31 , s31_host )
+        self.addLink( s32 , s32_host )
+        self.addLink( s33 , s33_host )
+        self.addLink( s34 , s34_host )
+        self.addLink( s35 , s35_host )
+        self.addLink( s36 , s36_host )
+        self.addLink( s37 , s37_host )
+        self.addLink( s38 , s38_host )
+        self.addLink( s39 , s39_host )
+        self.addLink( s40 , s40_host )
+        self.addLink( s41 , s41_host )
+        self.addLink( s42 , s42_host )
+        self.addLink( s43 , s43_host )
+        self.addLink( s44 , s44_host )
+        self.addLink( s45 , s45_host )
+        self.addLink( s46 , s46_host )
+        self.addLink( s47 , s47_host )
+        self.addLink( s48 , s48_host )
+        self.addLink( s49 , s49_host )
+        self.addLink( s50 , s50_host )
+        self.addLink( s51 , s51_host )
+        self.addLink( s52 , s52_host )
+        self.addLink( s53 , s53_host )
+        self.addLink( s54 , s54_host )
+        self.addLink( s55 , s55_host )
+        self.addLink( s56 , s56_host )
+        self.addLink( s57 , s57_host )
+        self.addLink( s58 , s58_host )
+        self.addLink( s59 , s59_host )
+        self.addLink( s60 , s60_host )
+        self.addLink( s61 , s61_host )
+        self.addLink( s62 , s62_host )
+        self.addLink( s63 , s63_host )
+        self.addLink( s64 , s64_host )
+        self.addLink( s65 , s65_host )
+        self.addLink( s66 , s66_host )
+        self.addLink( s67 , s67_host )
+        self.addLink( s68 , s68_host )
+        self.addLink( s69 , s69_host )
+        self.addLink( s70 , s70_host )
+        self.addLink( s71 , s71_host )
+        self.addLink( s72 , s72_host )
+        self.addLink( s73 , s73_host )
+        self.addLink( s74 , s74_host )
+        self.addLink( s75 , s75_host )
+        self.addLink( s76 , s76_host )
+        self.addLink( s77 , s77_host )
+        self.addLink( s78 , s78_host )
+
+        #info( '*** Add Leaf links\n')
+        self.addLink(s1, s9)
+        self.addLink(s2, s10)
+        self.addLink(s3, s9)
+        self.addLink(s4, s10)
+        self.addLink(s5, s9)
+        self.addLink(s6, s10)
+        self.addLink(s7, s9)
+        self.addLink(s8, s10)
+        self.addLink(s9, s11)
+        self.addLink(s9, s12)
+        self.addLink(s10, s13)
+        self.addLink(s10, s14)
+        self.addLink(s11, s12)
+        self.addLink(s13, s14)
+
+        #info( '*** Add Spine-1 links\n')
+        self.addLink(s15, s1)
+        self.addLink(s15, s2)
+        self.addLink(s16, s1)
+        self.addLink(s16, s2)
+        self.addLink(s17, s1)
+        self.addLink(s17, s2)
+        self.addLink(s18, s1)
+        self.addLink(s18, s2)
+        self.addLink(s19, s1)
+        self.addLink(s19, s2)
+        self.addLink(s20, s1)
+        self.addLink(s20, s2)
+        self.addLink(s21, s1)
+        self.addLink(s21, s2)
+        self.addLink(s22, s1)
+        self.addLink(s22, s2)
+        self.addLink(s23, s1)
+        self.addLink(s23, s2)
+        self.addLink(s24, s1)
+        self.addLink(s24, s2)
+        self.addLink(s25, s1)
+        self.addLink(s25, s2)
+        self.addLink(s26, s1)
+        self.addLink(s26, s2)
+        self.addLink(s27, s1)
+        self.addLink(s27, s2)
+        self.addLink(s28, s1)
+        self.addLink(s28, s2)
+        self.addLink(s29, s1)
+        self.addLink(s29, s2)
+        self.addLink(s30, s1)
+        self.addLink(s30, s2)
+
+        #info( '*** Add Spine-2 links\n')
+        self.addLink(s31, s3)
+        self.addLink(s31, s4)
+        self.addLink(s32, s3)
+        self.addLink(s32, s4)
+        self.addLink(s33, s3)
+        self.addLink(s33, s4)
+        self.addLink(s34, s3)
+        self.addLink(s34, s4)
+        self.addLink(s35, s3)
+        self.addLink(s35, s4)
+        self.addLink(s36, s3)
+        self.addLink(s36, s4)
+        self.addLink(s37, s3)
+        self.addLink(s37, s4)
+        self.addLink(s38, s3)
+        self.addLink(s38, s4)
+        self.addLink(s39, s3)
+        self.addLink(s39, s4)
+        self.addLink(s40, s3)
+        self.addLink(s40, s4)
+        self.addLink(s41, s3)
+        self.addLink(s41, s4)
+        self.addLink(s42, s3)
+        self.addLink(s42, s4)
+        self.addLink(s43, s3)
+        self.addLink(s43, s4)
+        self.addLink(s44, s3)
+        self.addLink(s44, s4)
+        self.addLink(s45, s3)
+        self.addLink(s45, s4)
+        self.addLink(s46, s3)
+        self.addLink(s46, s4)
+
+        #info( '*** Add Spine-3 links\n')
+        self.addLink(s47, s5)
+        self.addLink(s47, s6)
+        self.addLink(s48, s5)
+        self.addLink(s48, s6)
+        self.addLink(s49, s5)
+        self.addLink(s49, s6)
+        self.addLink(s50, s5)
+        self.addLink(s50, s6)
+        self.addLink(s51, s5)
+        self.addLink(s51, s6)
+        self.addLink(s52, s5)
+        self.addLink(s52, s6)
+        self.addLink(s53, s5)
+        self.addLink(s53, s6)
+        self.addLink(s54, s5)
+        self.addLink(s54, s6)
+        self.addLink(s55, s5)
+        self.addLink(s55, s6)
+        self.addLink(s56, s5)
+        self.addLink(s56, s6)
+        self.addLink(s57, s5)
+        self.addLink(s57, s6)
+        self.addLink(s58, s5)
+        self.addLink(s58, s6)
+        self.addLink(s59, s5)
+        self.addLink(s59, s6)
+        self.addLink(s60, s5)
+        self.addLink(s60, s6)
+        self.addLink(s61, s5)
+        self.addLink(s61, s6)
+        self.addLink(s62, s5)
+        self.addLink(s62, s6)
+
+        #info( '*** Add Spine-4 links\n')
+        self.addLink(s63, s7)
+        self.addLink(s63, s8)
+        self.addLink(s64, s7)
+        self.addLink(s64, s8)
+        self.addLink(s65, s7)
+        self.addLink(s65, s8)
+        self.addLink(s66, s7)
+        self.addLink(s66, s8)
+        self.addLink(s67, s7)
+        self.addLink(s67, s8)
+        self.addLink(s68, s7)
+        self.addLink(s68, s8)
+        self.addLink(s69, s7)
+        self.addLink(s69, s8)
+        self.addLink(s70, s7)
+        self.addLink(s70, s8)
+        self.addLink(s71, s7)
+        self.addLink(s71, s8)
+        self.addLink(s72, s7)
+        self.addLink(s72, s8)
+        self.addLink(s73, s7)
+        self.addLink(s73, s8)
+        self.addLink(s74, s7)
+        self.addLink(s74, s8)
+        self.addLink(s75, s7)
+        self.addLink(s75, s8)
+        self.addLink(s76, s7)
+        self.addLink(s76, s8)
+        self.addLink(s77, s7)
+        self.addLink(s77, s8)
+        self.addLink(s78, s7)
+        self.addLink(s78, s8)
+
+topos = { 'spine': ( lambda: spineTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = spineTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch, link=TCLink, autoSetMacs = True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/topoTripleIpv6.py b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoTripleIpv6.py
new file mode 100755
index 0000000..2a53b3d
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/topoTripleIpv6.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+"""
+"""
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import Host, RemoteController
+from mininet.node import Node
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.util import dumpNodeConnections
+from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
+
+class dualStackHost( Host ):
+    def config( self, v6Addr='1000::1/64', **params ):
+        r = super( Host, self ).config( **params )
+        intf = self.defaultIntf()
+        self.cmd( 'ip -6 addr add %s dev %s' % ( v6Addr, intf ) )
+        return r
+
+class tripleTopo( Topo ):
+
+    def __init__( self, **opts ):
+        "Create a topology."
+
+        # Initialize Topology
+        Topo.__init__( self, **opts )
+
+        s1 = self.addSwitch( 's1' )
+        s2 = self.addSwitch( 's2' )
+        s3 = self.addSwitch( 's3' )
+
+        # ... and now hosts
+        s1_host = self.addHost( 'h1', ip='10.1.0.1/24', cls=dualStackHost, v6Addr='1000::1/64' )
+        s2_host = self.addHost( 'h2', ip='10.1.0.2/24', cls=dualStackHost, v6Addr='1000::2/64' )
+        s3_host = self.addHost( 'h3', ip='10.1.0.3/24', cls=dualStackHost, v6Addr='1000::3/64' )
+
+        # add edges between switch and corresponding host
+        self.addLink( s1 , s1_host )
+        self.addLink( s2 , s2_host )
+        self.addLink( s3 , s3_host )
+        self.addLink(s1, s2)
+        self.addLink(s1, s3)
+        self.addLink(s2, s3)
+
+topos = { 'triple': ( lambda: tripleTopo() ) }
+
+# HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS
+
+def setupNetwork():
+    "Create network"
+    topo = tripleTopo()
+    #if controller_ip == '':
+        #controller_ip = '10.0.2.2';
+    #    controller_ip = '127.0.0.1';
+    network = Mininet(topo=topo, switch=OVSSwitch, autoSetMacs=True, controller=None)
+    network.start()
+    CLI( network )
+    network.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    #setLogLevel('debug')
+    setupNetwork()
diff --git a/TestON/tests/FUNC/FUNCflow/FUNCflow.params b/TestON/tests/FUNC/FUNCflow/FUNCflow.params
index 5defc95..aa7c358 100755
--- a/TestON/tests/FUNC/FUNCflow/FUNCflow.params
+++ b/TestON/tests/FUNC/FUNCflow/FUNCflow.params
@@ -4,10 +4,9 @@
     # 1,2,10,1000,1100,2000,1200,2000,100
     # 1 - Variable initialization and optional pull and build ONOS package
     # 2 - install ONOS
-    # 8 - Compare topology
-    # 9 - Report logs
     # 10 - Start mininet and verify topology
     # 66 - Testing Scapy
+    # 100 - Check logs for Errors and Warnings
     # 1000 - Add flows with MAC selector
     # 1100 - Add flows with IPv4 selector
     # 1200 - Add flows with VLAN selector
diff --git a/TestON/tests/FUNC/FUNCflow/FUNCflow.py b/TestON/tests/FUNC/FUNCflow/FUNCflow.py
index 7851d51..eeed451 100644
--- a/TestON/tests/FUNC/FUNCflow/FUNCflow.py
+++ b/TestON/tests/FUNC/FUNCflow/FUNCflow.py
@@ -305,8 +305,8 @@
         '''
         main.case( "Testing scapy" )
         main.step( "Creating Host1 component" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         for host in hosts:
             host.startHostCli()
@@ -317,10 +317,15 @@
             main.log.debug( host.hostMac )
 
         main.step( "Sending/Receiving Test packet - Filter doesn't match" )
+        main.log.info( "Starting Filter..." )
         main.h2.startFilter()
+        main.log.info( "Building Ether frame..." )
         main.h1.buildEther( dst=main.h2.hostMac )
+        main.log.info( "Sending Packet..." )
         main.h1.sendPacket( )
+        main.log.info( "Checking Filter..." )
         finished = main.h2.checkFilter()
+        main.log.debug( finished )
         i = ""
         if finished:
             a = main.h2.readPackets()
@@ -382,8 +387,8 @@
         main.step( "Add flows with MAC addresses as the only selectors" )
 
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         stepResult = main.TRUE
         for host in hosts:
@@ -504,8 +509,8 @@
         main.step( "Add flows with IPv4 addresses as the only selectors" )
 
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         stepResult = main.TRUE
         for host in hosts:
@@ -625,8 +630,8 @@
 
         # We do this here to utilize the hosts information
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h3" )
-        main.Mininet1.createHostComponent( "h4" )
+        main.Scapy.createHostComponent( "h3" )
+        main.Scapy.createHostComponent( "h4" )
         hosts = [main.h3, main.h4]
         stepResult = main.TRUE
         for host in hosts:
@@ -751,8 +756,8 @@
         main.step( "Add a flow with a MPLS selector" )
 
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         stepResult = main.TRUE
         for host in hosts:
@@ -867,8 +872,8 @@
         main.step( "Add a flow with a TCP selector" )
 
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         stepResult = main.TRUE
         for host in hosts:
@@ -991,8 +996,8 @@
         main.step( "Add a flow with a UDP selector" )
 
         main.log.info( "Creating host components" )
-        main.Mininet1.createHostComponent( "h1" )
-        main.Mininet1.createHostComponent( "h2" )
+        main.Scapy.createHostComponent( "h1" )
+        main.Scapy.createHostComponent( "h2" )
         hosts = [main.h1, main.h2]
         stepResult = main.TRUE
         for host in hosts:
diff --git a/TestON/tests/FUNC/FUNCflow/FUNCflow.topo b/TestON/tests/FUNC/FUNCflow/FUNCflow.topo
index d910446..50e5d63 100755
--- a/TestON/tests/FUNC/FUNCflow/FUNCflow.topo
+++ b/TestON/tests/FUNC/FUNCflow/FUNCflow.topo
@@ -23,7 +23,7 @@
         </ONOScli1>
 
         <Mininet1>
-            <host>localhost</host>
+            <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
@@ -42,5 +42,13 @@
             </COMPONENTS>
         </ONOSrest>
 
+        <Scapy>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>ScapyCliDriver</type>
+            <connect_order>7</connect_order>
+        </Scapy>
+
     </COMPONENT>
 </TOPOLOGY>
diff --git a/TestON/tests/FUNC/FUNCintent/FUNCintent.params b/TestON/tests/FUNC/FUNCintent/FUNCintent.params
index d0fcfa8..c201411 100644
--- a/TestON/tests/FUNC/FUNCintent/FUNCintent.params
+++ b/TestON/tests/FUNC/FUNCintent/FUNCintent.params
@@ -13,6 +13,7 @@
     # 16 - Balance ownership of switches
     # 17 - Activate Flow Objectives
     # 18 - Stop Mininet
+    # 19 - Copy karaf logs from ONOS nodes to TestON log directory
     # 1000 - Test host intents
     # 2000 - Test point intents
     # 3000 - Test single to multi point intents
@@ -20,7 +21,7 @@
     # 5000 - Test host mobility
     # 6000 - Test Multi Point intent End Point Failure
 
-    <testcases>1,[2,10,12,13,15,16,1000,2000,3000,4000,5000,6000,18]*2,[2,10,12,13,15,16,17,1000,2000,3000,4000,5000,6000,18]*2,[2,11,12,13,15,16,1000,2000,3000,4000,5000,6000,18]*2,[2,11,12,13,15,16,17,1000,2000,3000,4000,5000,6000,18]*2</testcases>
+    <testcases>1,[2,10,12,13,15,16,1000,2000,3000,4000,5000,6000,18,19]*2,[2,10,12,13,15,16,17,1000,2000,3000,4000,5000,6000,18,19]*2,[2,11,12,13,15,16,1000,2000,3000,4000,5000,6000,18,19]*2,[2,11,12,13,15,16,17,1000,2000,3000,4000,5000,6000,18,19]*2</testcases>
 
     <SCALE>
         <size>1,3,1,3,1,3,1,3</size>
diff --git a/TestON/tests/FUNC/FUNCintent/FUNCintent.py b/TestON/tests/FUNC/FUNCintent/FUNCintent.py
index ceb4b2d..ba0f2a9 100644
--- a/TestON/tests/FUNC/FUNCintent/FUNCintent.py
+++ b/TestON/tests/FUNC/FUNCintent/FUNCintent.py
@@ -59,6 +59,7 @@
             main.scapyHostNames = main.params[ 'SCAPY' ][ 'HOSTNAMES' ].split( ',' )
             main.scapyHosts = []  # List of scapy hosts for iterating
             main.assertReturnString = ''  # Assembled assert return string
+            main.cycle = 0 # How many times FUNCintent has run through its tests
 
             main.ONOSip = main.ONOSbench.getOnosIps()
             print main.ONOSip
@@ -132,6 +133,8 @@
         - Connect to cli
         """
 
+        main.cycle += 1
+
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.numCtrls = int( main.scale[ 0 ] )
         main.flowCompiler = "Flow Rules"
@@ -209,8 +212,9 @@
         onosIsUp = main.TRUE
 
         for i in range( main.numCtrls ):
-            onosIsUp = main.ONOSbench.isup( main.ONOSip[ i ] )
-            if onosIsUp == main.TRUE:
+            isUp = main.ONOSbench.isup( main.ONOSip[ i ] )
+            onosIsUp = onosIsUp and isUp
+            if isUp == main.TRUE:
                 main.log.report( "ONOS instance {0} is up and ready".format( i + 1 ) )
             else:
                 main.log.report( "ONOS instance {0} may not be up, stop and ".format( i + 1 ) +
@@ -654,6 +658,39 @@
             main.cleanup()
             main.exit()
 
+    def CASE19( self, main ):
+        """
+            Copy the karaf.log files after each testcase cycle
+        """
+        main.log.report( "Copy karaf logs" )
+        main.case( "Copy karaf logs" )
+        main.caseExplanation = "Copying the karaf logs to preserve them through" +\
+                               "reinstalling ONOS"
+        main.step( "Copying karaf logs" )
+        stepResult = main.TRUE
+        scpResult = main.TRUE
+        copyResult = main.TRUE
+        i = 0
+        for cli in main.CLIs:
+            main.node = cli
+            ip = main.ONOSip[ i ]
+            main.node.ip_address = ip
+            scpResult = scpResult and main.ONOSbench.scp( main.node ,
+                                            "/opt/onos/log/karaf.log",
+                                            "/tmp/karaf.log",
+                                            direction="from" )
+            copyResult = copyResult and main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,
+                                                                    copyFileName=( "karaf.log.node{0}.cycle{1}".format( str( i + 1 ), str( main.cycle ) ) ) )
+            if scpResult and copyResult:
+                stepResult =  main.TRUE and stepResult
+            else:
+                stepResult = main.FALSE and stepResult
+            i += 1
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully copied remote ONOS logs",
+                                 onfail="Failed to copy remote ONOS logs" )
+
     def CASE1000( self, main ):
         """
             Add host intents between 2 host:
@@ -835,8 +872,8 @@
 
         main.step( "VLAN1: Add vlan host intents between h4 and h12" )
         main.assertReturnString = "Assertion Result vlan IPV4\n"
-        host1 = { "name":"h4","id":"00:00:00:00:00:04/100" }
-        host2 = { "name":"h12","id":"00:00:00:00:00:0C/100 "}
+        host1 = { "name":"h4","id":"00:00:00:00:00:04/100", "vlan":"100" }
+        host2 = { "name":"h12","id":"00:00:00:00:00:0C/100", "vlan":"100" }
         testResult = main.FALSE
         installResult = main.FALSE
         installResult = main.intentFunction.installHostIntent( main,
@@ -863,10 +900,10 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString)
 
-        main.step( "VLAN2: Add inter vlan host intents between h13 and h20" )
-        main.assertReturnString = "Assertion Result different VLAN negative test\n"
-        host1 = { "name":"h13" }
-        host2 = { "name":"h20" }
+        main.step( "VLAN2: Add vlan host intents between h4 and h13" )
+        main.assertReturnString = "Assertion Result vlan IPV4\n"
+        host1 = { "name":"h5", "vlan":"200" }
+        host2 = { "name":"h12", "vlan":"100" }
         testResult = main.FALSE
         installResult = main.FALSE
         installResult = main.intentFunction.installHostIntent( main,
@@ -1105,7 +1142,8 @@
                                          recipients=recipients,
                                          sw1="s5",
                                          sw2="s2",
-                                         expectedLink=18)
+                                         expectedLink=18,
+                                         useTCP=True)
         else:
             main.CLIs[ 0 ].removeAllIntents( purge=True )
 
@@ -1183,25 +1221,57 @@
         main.step( "VLAN: Add point intents between h5 and h21" )
         main.assertReturnString = "Assertion Result for VLAN IPV4 with mac address point intents\n"
         senders = [
-            { "name":"h5","device":"of:0000000000000005/5","mac":"00:00:00:00:00:05" }
+            { "name":"h5","device":"of:0000000000000005/5","mac":"00:00:00:00:00:05", "vlan":"200" }
         ]
         recipients = [
-            { "name":"h21","device":"of:0000000000000007/5","mac":"00:00:00:00:00:15" }
+            { "name":"h21","device":"of:0000000000000007/5","mac":"00:00:00:00:00:15", "vlan":"200" }
         ]
         testResult = main.FALSE
         installResult = main.FALSE
         installResult = main.intentFunction.installPointIntent(
                                        main,
-                                       name="DUALSTACK1",
+                                       name="VLAN",
                                        senders=senders,
-                                       recipients=recipients,
-                                       ethType="IPV4" )
+                                       recipients=recipients)
 
         if installResult:
             testResult = main.intentFunction.testPointIntent(
                                          main,
                                          intentId=installResult,
-                                         name="DUALSTACK1",
+                                         name="VLAN",
+                                         senders=senders,
+                                         recipients=recipients,
+                                         sw1="s5",
+                                         sw2="s2",
+                                         expectedLink=18)
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "VLAN: Add point intents between h5 and h21" )
+        main.assertReturnString = "Assertion Result for VLAN IPV4 point intents with VLAN treatment\n"
+        senders = [
+            { "name":"h4", "vlan":"100" }
+        ]
+        recipients = [
+            { "name":"h21", "vlan":"200" }
+        ]
+        testResult = main.FALSE
+        installResult = main.FALSE
+        installResult = main.intentFunction.installPointIntent(
+                                       main,
+                                       name="VLAN2",
+                                       senders=senders,
+                                       recipients=recipients,
+                                       setVlan=200)
+
+        if installResult:
+            testResult = main.intentFunction.testPointIntent(
+                                         main,
+                                         intentId=installResult,
+                                         name="VLAN2",
                                          senders=senders,
                                          recipients=recipients,
                                          sw1="s5",
@@ -1433,11 +1503,11 @@
         main.step( "VLAN: Add single point to multi point intents" )
         main.assertReturnString = "Assertion results for IPV4 single to multi point intent with IPV4 type and MAC addresses in the same VLAN\n"
         senders = [
-            { "name":"h4", "device":"of:0000000000000005/4", "mac":"00:00:00:00:00:04" }
+            { "name":"h4", "device":"of:0000000000000005/4", "mac":"00:00:00:00:00:04", "vlan":"100" }
         ]
         recipients = [
-            { "name":"h12", "device":"of:0000000000000006/4", "mac":"00:00:00:00:00:0C" },
-            { "name":"h20", "device":"of:0000000000000007/4", "mac":"00:00:00:00:00:14" }
+            { "name":"h12", "device":"of:0000000000000006/4", "mac":"00:00:00:00:00:0C", "vlan":"100" },
+            { "name":"h20", "device":"of:0000000000000007/4", "mac":"00:00:00:00:00:14", "vlan":"100" }
         ]
         badSenders=[ { "name":"h13" } ]  # Senders that are not in the intent
         badRecipients=[ { "name":"h21" } ]  # Recipients that are not in the intent
@@ -1445,10 +1515,9 @@
         installResult = main.FALSE
         installResult = main.intentFunction.installSingleToMultiIntent(
                                          main,
-                                         name="IPV4",
+                                         name="VLAN`",
                                          senders=senders,
                                          recipients=recipients,
-                                         ethType="IPV4",
                                          sw1="s5",
                                          sw2="s2")
 
@@ -1456,7 +1525,49 @@
             testResult = main.intentFunction.testPointIntent(
                                          main,
                                          intentId=installResult,
-                                         name="IPV4",
+                                         name="VLAN",
+                                         senders=senders,
+                                         recipients=recipients,
+                                         badSenders=badSenders,
+                                         badRecipients=badRecipients,
+                                         sw1="s5",
+                                         sw2="s2",
+                                         expectedLink=18)
+        else:
+            main.CLIs[ 0 ].removeAllIntents( purge=True )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "VLAN: Add single point to multi point intents" )
+        main.assertReturnString = "Assertion results for single to multi point intent with VLAN treatment\n"
+        senders = [
+            { "name":"h5", "vlan":"200" }
+        ]
+        recipients = [
+            { "name":"h12", "device":"of:0000000000000006/4", "mac":"00:00:00:00:00:0C", "vlan":"100" },
+            { "name":"h20", "device":"of:0000000000000007/4", "mac":"00:00:00:00:00:14", "vlan":"100" }
+        ]
+        badSenders=[ { "name":"h13" } ]  # Senders that are not in the intent
+        badRecipients=[ { "name":"h21" } ]  # Recipients that are not in the intent
+        testResult = main.FALSE
+        installResult = main.FALSE
+        installResult = main.intentFunction.installSingleToMultiIntent(
+                                         main,
+                                         name="VLAN2",
+                                         senders=senders,
+                                         recipients=recipients,
+                                         sw1="s5",
+                                         sw2="s2",
+                                         setVlan=100)
+
+        if installResult:
+            testResult = main.intentFunction.testPointIntent(
+                                         main,
+                                         intentId=installResult,
+                                         name="VLAN2",
                                          senders=senders,
                                          recipients=recipients,
                                          badSenders=badSenders,
@@ -1657,11 +1768,11 @@
         main.step( "VLAN: Add multi point to single point intents" )
         main.assertReturnString = "Assertion results for IPV4 multi to single point intent with IPV4 type and no MAC addresses in the same VLAN\n"
         senders = [
-            { "name":"h13", "device":"of:0000000000000006/5" },
-            { "name":"h21", "device":"of:0000000000000007/5" }
+            { "name":"h13", "device":"of:0000000000000006/5", "vlan":"200" },
+            { "name":"h21", "device":"of:0000000000000007/5", "vlan":"200" }
         ]
         recipients = [
-            { "name":"h5", "device":"of:0000000000000005/5" }
+            { "name":"h5", "device":"of:0000000000000005/5", "vlan":"200" }
         ]
         badSenders=[ { "name":"h12" } ]  # Senders that are not in the intent
         badRecipients=[ { "name":"h20" } ]  # Recipients that are not in the intent
@@ -1672,7 +1783,6 @@
                                          name="VLAN",
                                          senders=senders,
                                          recipients=recipients,
-                                         ethType="IPV4",
                                          sw1="s5",
                                          sw2="s2")
 
@@ -1696,6 +1806,49 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
 
+        # Right now this fails because of this bug: https://jira.onosproject.org/browse/ONOS-4383
+        main.step( "VLAN: Add multi point to single point intents" )
+        main.assertReturnString = "Assertion results for multi to single point intent with VLAN ID treatment\n"
+        senders = [
+            { "name":"h13", "device":"of:0000000000000006/5", "vlan":"200" },
+            { "name":"h21", "device":"of:0000000000000007/5", "vlan":"200" }
+        ]
+        recipients = [
+            { "name":"h4", "vlan":"100" }
+        ]
+        badSenders=[ { "name":"h12" } ]  # Senders that are not in the intent
+        badRecipients=[ { "name":"h20" } ]  # Recipients that are not in the intent
+        testResult = main.FALSE
+        installResult = main.FALSE
+        installResult = main.intentFunction.installMultiToSingleIntent(
+                                         main,
+                                         name="VLAN2",
+                                         senders=senders,
+                                         recipients=recipients,
+                                         sw1="s5",
+                                         sw2="s2",
+                                         setVlan=100)
+
+        if installResult:
+            testResult = main.intentFunction.testPointIntent(
+                                         main,
+                                         intentId=installResult,
+                                         name="VLAN2",
+                                         senders=senders,
+                                         recipients=recipients,
+                                         badSenders=badSenders,
+                                         badRecipients=badRecipients,
+                                         sw1="s5",
+                                         sw2="s2",
+                                         expectedLink=18)
+        else:
+            main.CLIs[ 0 ].removeAllIntents( purge=True )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
         main.intentFunction.report( main )
 
     def CASE5000( self, main ):
@@ -1789,6 +1942,9 @@
         """
         Tests Multi to Single Point Intent and Single to Multi Point Intent End Point Failure
         """
+        # At some later point discussion on this behavior in MPSP and SPMP intents
+        # will be reoppened and this test case may need to be updated to reflect
+        # the outcomes of that discussion
         if main.initialized == main.FALSE:
             main.log.error( "Test components did not start correctly, skipping further tests" )
             main.skipCase()
@@ -1813,10 +1969,9 @@
             main.initialized = main.FALSE
             main.skipCase()
         main.case( "Test Multi to Single End Point Failure" )
-        main.step( "Installing Multi to Single Point intents" )
-
-        main.assertReturnString = "Assertion results for IPV4 multi to single \
-                                  point intent end point failure with no options set\n"
+        main.step( "Installing Multi to Single Point intents with no options set" )
+        main.assertReturnString = "Assertion results for IPV4 multi to single " +\
+                                  "point intent end point failure with no options set\n"
         senders = [
             { "name":"h16", "device":"of:0000000000000006/8" },
             { "name":"h24", "device":"of:0000000000000007/8" }
@@ -1862,58 +2017,10 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
 
-        main.step( "IPV4: Add multi point to single point intents" )
-        main.assertReturnString = "Assertion results for IPV4 multi to single \
-                                  point intent end point failure with IPV4 type and MAC addresses\n"
-        senders = [
-            { "name":"h16", "device":"of:0000000000000006/8", "mac":"00:00:00:00:00:10" },
-            { "name":"h24", "device":"of:0000000000000007/8", "mac":"00:00:00:00:00:18" }
-        ]
-        recipients = [
-            { "name":"h8", "device":"of:0000000000000005/8", "mac":"00:00:00:00:00:08" }
-        ]
-        isolatedSenders = [
-            { "name":"h24"}
-        ]
-        isolatedRecipients = []
-        testResult = main.FALSE
-        installResult = main.FALSE
-        installResult = main.intentFunction.installMultiToSingleIntent(
-                                         main,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         ethType="IPV4",
-                                         sw1="s5",
-                                         sw2="s2")
+        main.step( "Installing Multi to Single Point intents with partial failure allowed" )
 
-        if installResult:
-            testResult = main.intentFunction.testEndPointFail(
-                                         main,
-                                         intentId=installResult,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         isolatedSenders=isolatedSenders,
-                                         isolatedRecipients=isolatedRecipients,
-                                         sw1="s6",
-                                         sw2="s2",
-                                         sw3="s4",
-                                         sw4="s1",
-                                         sw5="s3",
-                                         expectedLink1=16,
-                                         expectedLink2=14 )
-        else:
-            main.CLIs[ 0 ].removeAllIntents( purge=True )
-
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=testResult,
-                                 onpass=main.assertReturnString,
-                                 onfail=main.assertReturnString )
-
-        main.step( "IPV4_2: Add multi point to single point intents" )
-        main.assertReturnString = "Assertion results for IPV4 multi to single \
-                                  point intent end point failure with IPV4 type and no MAC addresses\n"
+        main.assertReturnString = "Assertion results for IPV4 multi to single " +\
+                                  "with partial failures allowed\n"
         senders = [
             { "name":"h16", "device":"of:0000000000000006/8" },
             { "name":"h24", "device":"of:0000000000000007/8" }
@@ -1929,18 +2036,18 @@
         installResult = main.FALSE
         installResult = main.intentFunction.installMultiToSingleIntent(
                                          main,
-                                         name="IPV4_2",
+                                         name="NOOPTION",
                                          senders=senders,
                                          recipients=recipients,
-                                         ethType="IPV4",
                                          sw1="s5",
-                                         sw2="s2")
+                                         sw2="s2",
+                                         partial=True )
 
         if installResult:
             testResult = main.intentFunction.testEndPointFail(
                                          main,
                                          intentId=installResult,
-                                         name="IPV4_2",
+                                         name="NOOPTION",
                                          senders=senders,
                                          recipients=recipients,
                                          isolatedSenders=isolatedSenders,
@@ -1951,56 +2058,8 @@
                                          sw4="s1",
                                          sw5="s3",
                                          expectedLink1=16,
-                                         expectedLink2=14 )
-        else:
-            main.CLIs[ 0 ].removeAllIntents( purge=True )
-
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=testResult,
-                                 onpass=main.assertReturnString,
-                                 onfail=main.assertReturnString )
-
-        main.step( "VLAN: Add multi point to single point intents" )
-        main.assertReturnString = "Assertion results for IPV4 multi to single \
-                                  point intent end point failure with IPV4 type and no MAC addresses in the same VLAN\n"
-        senders = [
-            { "name":"h13", "device":"of:0000000000000006/5" },
-            { "name":"h21", "device":"of:0000000000000007/5" }
-        ]
-        recipients = [
-            { "name":"h5", "device":"of:0000000000000005/5" }
-        ]
-        isolatedSenders = [
-            { "name":"h21"}
-        ]
-        isolatedRecipients = []
-        testResult = main.FALSE
-        installResult = main.FALSE
-        installResult = main.intentFunction.installMultiToSingleIntent(
-                                         main,
-                                         name="VLAN",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         ethType="IPV4",
-                                         sw1="s5",
-                                         sw2="s2")
-
-        if installResult:
-            testResult = main.intentFunction.testEndPointFail(
-                                         main,
-                                         intentId=installResult,
-                                         name="VLAN",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         isolatedSenders=isolatedSenders,
-                                         isolatedRecipients=isolatedRecipients,
-                                         sw1="s6",
-                                         sw2="s2",
-                                         sw3="s4",
-                                         sw4="s1",
-                                         sw5="s3",
-                                         expectedLink1=16,
-                                         expectedLink2=14 )
+                                         expectedLink2=14,
+                                         partial=True )
         else:
             main.CLIs[ 0 ].removeAllIntents( purge=True )
 
@@ -2010,8 +2069,8 @@
                                  onfail=main.assertReturnString )
 
         main.step( "NOOPTION: Install and test single point to multi point intents" )
-        main.assertReturnString = "Assertion results for IPV4 single to multi \
-                                  point intent end point failure with no options set\n"
+        main.assertReturnString = "Assertion results for IPV4 single to multi " +\
+                                  "point intent with no options set\n"
         senders = [
             { "name":"h8", "device":"of:0000000000000005/8" }
         ]
@@ -2021,7 +2080,7 @@
         ]
         isolatedSenders = []
         isolatedRecipients = [
-            { "name":"h24" }
+            { "name":"h24"}
         ]
         testResult = main.FALSE
         installResult = main.FALSE
@@ -2056,59 +2115,11 @@
                                  actual=testResult,
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
-
-        main.step( "IPV4: Install and test single point to multi point intents" )
-        main.assertReturnString = "Assertion results for IPV4 single to multi \
-                                  point intent end point failure with IPV4 type and no MAC addresses\n"
-        senders = [
-            { "name":"h8", "device":"of:0000000000000005/8","mac":"00:00:00:00:00:08" }
-        ]
-        recipients = [
-            { "name":"h16", "device":"of:0000000000000006/8", "mac":"00:00:00:00:00:10" },
-            { "name":"h24", "device":"of:0000000000000007/8", "mac":"00:00:00:00:00:18" }
-        ]
-        isolatedSenders = []
-        isolatedRecipients = [
-            { "name":"h24" }
-        ]
-        testResult = main.FALSE
-        installResult = main.FALSE
-        installResult = main.intentFunction.installSingleToMultiIntent(
-                                         main,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         ethType="IPV4",
-                                         sw1="s5",
-                                         sw2="s2")
-
-        if installResult:
-            testResult = main.intentFunction.testEndPointFail(
-                                         main,
-                                         intentId=installResult,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         isolatedSenders=isolatedSenders,
-                                         isolatedRecipients=isolatedRecipients,
-                                         sw1="s6",
-                                         sw2="s2",
-                                         sw3="s4",
-                                         sw4="s1",
-                                         sw5="s3",
-                                         expectedLink1=16,
-                                         expectedLink2=14 )
-        else:
-            main.CLIs[ 0 ].removeAllIntents( purge=True )
-
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=testResult,
-                                 onpass=main.assertReturnString,
-                                 onfail=main.assertReturnString )
-
-        main.step( "IPV4_2: Add single point to multi point intents" )
-        main.assertReturnString = "Assertion results for IPV4 single to multi\
-                                  point intent endpoint failure with IPV4 type and no MAC addresses\n"
+        # Right now this functionality doesn't work properly in SPMP intents
+        main.step( "NOOPTION: Install and test single point to multi point " +\
+                   "intents with partial failures allowed" )
+        main.assertReturnString = "Assertion results for IPV4 single to multi " +\
+                                  "point intent with partial failures allowed\n"
         senders = [
             { "name":"h8", "device":"of:0000000000000005/8" }
         ]
@@ -2118,24 +2129,24 @@
         ]
         isolatedSenders = []
         isolatedRecipients = [
-            { "name":"h24" }
+            { "name":"h24"}
         ]
         testResult = main.FALSE
         installResult = main.FALSE
         installResult = main.intentFunction.installSingleToMultiIntent(
                                          main,
-                                         name="IPV4_2",
+                                         name="NOOPTION",
                                          senders=senders,
                                          recipients=recipients,
-                                         ethType="IPV4",
                                          sw1="s5",
-                                         sw2="s2")
+                                         sw2="s2",
+                                         partial=True)
 
         if installResult:
             testResult = main.intentFunction.testEndPointFail(
                                          main,
                                          intentId=installResult,
-                                         name="IPV4_2",
+                                         name="NOOPTION",
                                          senders=senders,
                                          recipients=recipients,
                                          isolatedSenders=isolatedSenders,
@@ -2146,56 +2157,8 @@
                                          sw4="s1",
                                          sw5="s3",
                                          expectedLink1=16,
-                                         expectedLink2=14 )
-        else:
-            main.CLIs[ 0 ].removeAllIntents( purge=True )
-
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=testResult,
-                                 onpass=main.assertReturnString,
-                                 onfail=main.assertReturnString )
-
-        main.step( "VLAN: Add single point to multi point intents" )
-        main.assertReturnString = "Assertion results for IPV4 single to multi point\
-                                  intent endpoint failure with IPV4 type and MAC addresses in the same VLAN\n"
-        senders = [
-            { "name":"h4", "device":"of:0000000000000005/4", "mac":"00:00:00:00:00:04" }
-        ]
-        recipients = [
-            { "name":"h12", "device":"of:0000000000000006/4", "mac":"00:00:00:00:00:0C" },
-            { "name":"h20", "device":"of:0000000000000007/4", "mac":"00:00:00:00:00:14" }
-        ]
-        isolatedSenders = []
-        isolatedRecipients = [
-            { "name":"h20" }
-        ]
-        testResult = main.FALSE
-        installResult = main.FALSE
-        installResult = main.intentFunction.installSingleToMultiIntent(
-                                         main,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         ethType="IPV4",
-                                         sw1="s5",
-                                         sw2="s2")
-
-        if installResult:
-            testResult = main.intentFunction.testEndPointFail(
-                                         main,
-                                         intentId=installResult,
-                                         name="IPV4",
-                                         senders=senders,
-                                         recipients=recipients,
-                                         isolatedSenders=isolatedSenders,
-                                         isolatedRecipients=isolatedRecipients,
-                                         sw1="s6",
-                                         sw2="s2",
-                                         sw3="s4",
-                                         sw4="s1",
-                                         sw5="s3",
-                                         expectedLink1=16,
-                                         expectedLink2=14 )
+                                         expectedLink2=14,
+                                         partial=True )
         else:
             main.CLIs[ 0 ].removeAllIntents( purge=True )
 
diff --git a/TestON/tests/FUNC/FUNCintent/dependencies/FuncIntentFunction.py b/TestON/tests/FUNC/FUNCintent/dependencies/FuncIntentFunction.py
index dbafffa..1caa472 100644
--- a/TestON/tests/FUNC/FUNCintent/dependencies/FuncIntentFunction.py
+++ b/TestON/tests/FUNC/FUNCintent/dependencies/FuncIntentFunction.py
@@ -22,7 +22,8 @@
                        ipAddresses="",
                        tcp="",
                        sw1="",
-                       sw2=""):
+                       sw2="",
+                       setVlan="" ):
     """
     Installs a Host Intent
 
@@ -75,8 +76,11 @@
             host2[ "id" ] = main.hostsData.get( host2.get( "name" ) ).get( "id" )
 
         # Adding point intent
+        vlanId = host1.get( "vlan" )
         intentId = main.CLIs[ onosNode ].addHostIntent( hostIdOne=host1.get( "id" ),
-                                                        hostIdTwo=host2.get( "id" ) )
+                                                        hostIdTwo=host2.get( "id" ),
+                                                        vlanId=vlanId,
+                                                        setVlan=setVlan )
     except (KeyError, TypeError):
         errorMsg = "There was a problem loading the hosts data."
         if intentId:
@@ -168,6 +172,7 @@
 
         senderNames = [ host1.get( "name" ), host2.get( "name" ) ]
         recipientNames = [ host1.get( "name" ), host2.get( "name" ) ]
+        vlanId = host1.get( "vlan" )
 
         testResult = main.TRUE
     except (KeyError, TypeError):
@@ -191,7 +196,7 @@
         testResult = main.FALSE
 
     # Check Connectivity
-    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
         main.assertReturnString += 'Initial Ping Passed\n'
     else:
         main.assertReturnString += 'Initial Ping Failed\n'
@@ -228,7 +233,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
             main.assertReturnString += 'Link Down Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Down Pingall Failed\n'
@@ -266,7 +271,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
             main.assertReturnString += 'Link Up Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Up Pingall Failed\n'
@@ -293,7 +298,8 @@
                         ipSrc="",
                         ipDst="",
                         tcpSrc="",
-                        tcpDst=""):
+                        tcpDst="",
+                        setVlan=""):
     """
     Installs a Single to Single Point Intent
 
@@ -364,6 +370,8 @@
         ipSrc = senders[ 0 ].get( "ip" )
         ipDst = recipients[ 0 ].get( "ip" )
 
+        vlanId = senders[ 0 ].get( "vlan" )
+
         # Adding point intent
         intentId = main.CLIs[ onosNode ].addPointIntent(
                                             ingressDevice=ingressDevice,
@@ -378,7 +386,9 @@
                                             ipSrc=ipSrc,
                                             ipDst=ipDst,
                                             tcpSrc=tcpSrc,
-                                            tcpDst=tcpDst )
+                                            tcpDst=tcpDst,
+                                            vlanId=vlanId,
+                                            setVlan=setVlan )
     except (KeyError, TypeError):
         errorMsg = "There was a problem loading the hosts data."
         if intentId:
@@ -688,7 +698,9 @@
                                 ipAddresses="",
                                 tcp="",
                                 sw1="",
-                                sw2=""):
+                                sw2="",
+                                setVlan="",
+                                partial=False ):
     """
     Installs a Single to Multi Point Intent
 
@@ -758,6 +770,7 @@
             portEgressList = None
 
         srcMac = senders[ 0 ].get( "mac" )
+        vlanId = senders[ 0 ].get( "vlan" )
 
         # Adding point intent
         intentId = main.CLIs[ onosNode ].addSinglepointToMultipointIntent(
@@ -773,7 +786,10 @@
                                             ipSrc="",
                                             ipDst="",
                                             tcpSrc="",
-                                            tcpDst="" )
+                                            tcpDst="",
+                                            vlanId=vlanId,
+                                            setVlan=setVlan,
+                                            partial=partial )
     except (KeyError, TypeError):
         errorMsg = "There was a problem loading the hosts data."
         if intentId:
@@ -800,7 +816,9 @@
                                 ipAddresses="",
                                 tcp="",
                                 sw1="",
-                                sw2=""):
+                                sw2="",
+                                setVlan="",
+                                partial=False ):
     """
     Installs a Multi to Single Point Intent
 
@@ -869,6 +887,7 @@
             portIngressList = None
 
         dstMac = recipients[ 0 ].get( "mac" )
+        vlanId = senders[ 0 ].get( "vlan" )
 
         # Adding point intent
         intentId = main.CLIs[ onosNode ].addMultipointToSinglepointIntent(
@@ -884,7 +903,10 @@
                                             ipSrc="",
                                             ipDst="",
                                             tcpSrc="",
-                                            tcpDst="" )
+                                            tcpDst="",
+                                            vlanId=vlanId,
+                                            setVlan=setVlan,
+                                            partial=partial )
     except (KeyError, TypeError):
         errorMsg = "There was a problem loading the hosts data."
         if intentId:
@@ -915,7 +937,8 @@
                      tcp="",
                      sw1="s5",
                      sw2="s2",
-                     expectedLink=0):
+                     expectedLink=0,
+                     useTCP=False):
     """
     Test a Point Intent
 
@@ -993,6 +1016,7 @@
             if not recipient.get( "device" ):
                 main.log.warn( "Device not given for recipient {0}. Loading from main.hostData".format( recipient.get( "name" ) ) )
                 recipient[ "device" ] = main.hostsData.get( recipient.get( "name" ) ).get( "location" )
+        vlanId = senders[ 0 ].get( "vlan" )
     except (KeyError, TypeError):
         main.log.error( "There was a problem loading the hosts data." )
         return main.FALSE
@@ -1015,7 +1039,7 @@
         testResult = main.FALSE
 
     # Check Connectivity
-    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId, useTCP ), attempts=3, sleep=5 ):
         main.assertReturnString += 'Initial Ping Passed\n'
     else:
         main.assertReturnString += 'Initial Ping Failed\n'
@@ -1069,7 +1093,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId, useTCP ) ):
             main.assertReturnString += 'Link Down Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Down Pingall Failed\n'
@@ -1107,7 +1131,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId, useTCP ) ):
             main.assertReturnString += 'Link Up Scapy Packet Received Passed\n'
         else:
             main.assertReturnString += 'Link Up Scapy Packet Recieved Failed\n'
@@ -1142,7 +1166,8 @@
                       sw4="",
                       sw5="",
                       expectedLink1=0,
-                      expectedLink2=0 ):
+                      expectedLink2=0,
+                      partial=False ):
     """
     Test Single to Multipoint Topology for Endpoint failures
     """
@@ -1195,9 +1220,9 @@
 
     # Check flows count in each node
     if utilities.retry( f=checkFlowsCount, retValue=main.FALSE,
-                        args=[ main ] ) and utilities.retry( f=checkFlowsState,
-                                                             retValue=main.FALSE,
-                                                             args=[ main ] ):
+                        args=[ main ], attempts=5 ) and utilities.retry( f=checkFlowsState,
+                                                                         retValue=main.FALSE,
+                                                                         args=[ main ], attempts=5 ):
         main.assertReturnString += 'Initial Flow State Passed\n'
     else:
         main.assertReturnString += 'Intial Flow State Failed\n'
@@ -1265,55 +1290,107 @@
         main.assertReturnString += 'Isolation link Down Failed\n'
         testResult = main.FALSE
 
-    # Check intent state
-    if utilities.retry( f=checkIntentState, retValue=main.FALSE,
-                        args=( main, [ intentId ] ), sleep=main.checkIntentSleep ):
-        main.assertReturnString += 'Isolation link Down Intent State Passed\n'
-    else:
-        main.assertReturnString += 'Isolation link Down Intent State Failed\n'
-        testResult = main.FALSE
+    if partial:
+        # Check intent state
+        if utilities.retry( f=checkIntentState, retValue=main.FALSE,
+                            args=( main, [ intentId ] ), sleep=main.checkIntentSleep ):
+            main.assertReturnString += 'Partial failure isolation link Down Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Partial failure isolation link Down Intent State Failed\n'
+            testResult = main.FALSE
 
-    # Check flows count in each node
-    if utilities.retry( f=checkFlowsCount, retValue=main.FALSE,
-                        args=[ main ] ) and utilities.retry( f=checkFlowsState,
-                                                             retValue=main.FALSE, args=[ main ] ):
-        main.assertReturnString += 'Isolation link Down Flow State Passed\n'
-    else:
-        main.assertReturnString += 'Isolation link Down Flow State Failed\n'
-        testResult = main.FALSE
+        # Check flows count in each node
+        if utilities.retry( f=checkFlowsCount, retValue=main.FALSE,
+                            args=[ main ], attempts=5 ) and utilities.retry( f=checkFlowsState,
+                                                                             retValue=main.FALSE,
+                                                                             args=[ main ], attempts=5 ):
+            main.assertReturnString += 'Partial failure isolation link Down Flow State Passed\n'
+        else:
+            main.assertReturnString += 'Partial failure isolation link Down Flow State Failed\n'
+            testResult = main.FALSE
 
-    # Check OnosTopology
-    if utilities.retry( f=checkTopology, retValue=main.FALSE, args=( main, expectedLink2 ) ):
-        main.assertReturnString += 'Isolation link Down Topology State Passed\n'
-    else:
-        main.assertReturnString += 'Isolation link Down Topology State Failed\n'
-        testResult = main.FALSE
+        # Check OnosTopology
+        if utilities.retry( f=checkTopology, retValue=main.FALSE, args=( main, expectedLink2 ) ):
+            main.assertReturnString += 'Partial failure isolation link Down Topology State Passed\n'
+        else:
+            main.assertReturnString += 'Partial failure isolation link Down Topology State Failed\n'
+            testResult = main.FALSE
 
-    # Check Connectivity
-    # First check connectivity of any isolated senders to recipients
-    if isolatedSenderNames:
-        if scapyCheckConnection( main, isolatedSenderNames, recipientNames, None, None, main.TRUE ):
+        # Check Connectivity
+        # First check connectivity of any isolated senders to recipients
+        if isolatedSenderNames:
+            if scapyCheckConnection( main, isolatedSenderNames, recipientNames, None, None, None, None, main.TRUE ):
+                main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Passed\n'
+            else:
+                main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Failed\n'
+                testResult = main.FALSE
+
+        # Next check connectivity of senders to any isolated recipients
+        if isolatedRecipientNames:
+            if scapyCheckConnection( main, senderNames, isolatedRecipientNames, None, None, None, None, main.TRUE ):
+                main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Passed\n'
+            else:
+                main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Failed\n'
+                testResult = main.FALSE
+
+        # Next check connectivity of connected senders and recipients
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE,
+                            args=( main, connectedSenderNames , connectedRecipientNames ) ):
+            main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Passed\n'
+        else:
+            main.assertReturnString += 'Partial failure isolation link Down Connectivity Check Failed\n'
+            testResult = main.FALSE
+    else:
+        # Check intent state
+        if not utilities.retry( f=checkIntentState, retValue=main.TRUE,
+                            args=( main, [ intentId ] ), sleep=main.checkIntentSleep ):
+            main.assertReturnString += 'Isolation link Down Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation link Down Intent State Failed\n'
+            testResult = main.FALSE
+
+        # Check flows count in each node
+        if utilities.retry( f=checkFlowsCount, retValue=main.FALSE,
+                            args=[ main ], attempts=5 ) and utilities.retry( f=checkFlowsState,
+                                                                             retValue=main.FALSE,
+                                                                             args=[ main ], attempts=5 ):
+            main.assertReturnString += 'Isolation link Down Flow State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation link Down Flow State Failed\n'
+            testResult = main.FALSE
+
+        # Check OnosTopology
+        if utilities.retry( f=checkTopology, retValue=main.FALSE, args=( main, expectedLink2 ) ):
+            main.assertReturnString += 'Isolation link Down Topology State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation link Down Topology State Failed\n'
+            testResult = main.FALSE
+
+        # Check Connectivity
+        # First check connectivity of any isolated senders to recipients
+        if isolatedSenderNames:
+            if scapyCheckConnection( main, isolatedSenderNames, recipientNames, None, None, None, None, main.TRUE ):
+                main.assertReturnString += 'Isolation link Down Connectivity Check Passed\n'
+            else:
+                main.assertReturnString += 'Isolation link Down Connectivity Check Failed\n'
+                testResult = main.FALSE
+
+        # Next check connectivity of senders to any isolated recipients
+        if isolatedRecipientNames:
+            if scapyCheckConnection( main, senderNames, isolatedRecipientNames, None, None, None, None, main.TRUE ):
+                main.assertReturnString += 'Isolation link Down Connectivity Check Passed\n'
+            else:
+                main.assertReturnString += 'Isolation link Down Connectivity Check Failed\n'
+                testResult = main.FALSE
+
+        # Next check connectivity of connected senders and recipients
+        if utilities.retry( f=scapyCheckConnection, retValue=main.TRUE,
+                            args=( main, connectedSenderNames , connectedRecipientNames, None, None, None, None, main.TRUE ) ):
             main.assertReturnString += 'Isolation link Down Connectivity Check Passed\n'
         else:
             main.assertReturnString += 'Isolation link Down Connectivity Check Failed\n'
             testResult = main.FALSE
 
-    # Next check connectivity of senders to any isolated recipients
-    if isolatedRecipientNames:
-        if scapyCheckConnection( main, senderNames, isolatedRecipientNames, None, None, main.TRUE ):
-            main.assertReturnString += 'Isolation link Down Connectivity Check Passed\n'
-        else:
-            main.assertReturnString += 'Isolation link Down Connectivity Check Failed\n'
-            testResult = main.FALSE
-
-    # Next check connectivity of connected senders and recipients
-    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE,
-                        args=( main, connectedSenderNames , connectedRecipientNames ) ):
-        main.assertReturnString += 'Isolation link Down Connectivity Check Passed\n'
-    else:
-        main.assertReturnString += 'Isolation link Down Connectivity Check Failed\n'
-        testResult = main.FALSE
-
     # Bring the links back up
     # Bring first link up
     if utilities.retry( f=link, retValue=main.FALSE, args=( main, sw1, sw2, "up" ) ):
@@ -1349,8 +1426,9 @@
 
     # Check flows count in each node
     if utilities.retry( f=checkFlowsCount, retValue=main.FALSE,
-                        args=[ main ] ) and utilities.retry( f=checkFlowsState,
-                                                             retValue=main.FALSE, args=[ main ] ):
+                        args=[ main ], sleep=5, attempts=5 ) and utilities.retry( f=checkFlowsState,
+                                                                         retValue=main.FALSE,
+                                                                         args=[ main ], sleep=5, attempts=5 ):
         main.assertReturnString += 'Link Up Flow State Passed\n'
     else:
         main.assertReturnString += 'Link Up Flow State Failed\n'
@@ -1530,7 +1608,7 @@
 
     for i in range( main.numCtrls ):
         topologyResult = main.CLIs[ i ].topology()
-        statusResult = main.ONOSbench.checkStatus( topologyResult,
+        statusResult = main.CLIs[ i ].checkStatus( topologyResult,
                                                    main.numSwitch,
                                                    expectedLink )\
                        and statusResult
@@ -1581,7 +1659,7 @@
 def checkFlowsState( main ):
 
     main.log.info( itemName + ": Check flows state" )
-    checkFlowsResult = main.CLIs[ 0 ].checkFlowsState()
+    checkFlowsResult = main.CLIs[ 0 ].checkFlowsState( isPENDING=False )
     return checkFlowsResult
 
 def link( main, sw1, sw2, option):
@@ -1592,7 +1670,7 @@
     linkResult = main.Mininet1.link( end1=sw1, end2=sw2, option=option )
     return linkResult
 
-def scapyCheckConnection( main, senders, recipients, packet=None, packetFilter=None, expectFailure=False ):
+def scapyCheckConnection( main, senders, recipients, vlanId=None, useTCP=False, packet=None, packetFilter=None, expectFailure=False ):
     """
         Checks the connectivity between all given sender hosts and all given recipient hosts
         Packet may be specified. Defaults to Ether/IP packet
@@ -1606,7 +1684,8 @@
 
     if not packetFilter:
         packetFilter = 'ether host {}'
-
+    if useTCP:
+        packetFilter += ' ip proto \\tcp tcp port {}'.format(main.params[ 'SDNIP' ][ 'dstPort' ])
     if expectFailure:
         timeout = 1
     else:
@@ -1631,17 +1710,31 @@
                 connectionsFunctional = main.FALSE
                 continue
 
-            recipientComp.startFilter( pktFilter = packetFilter.format( senderComp.hostMac ) )
+            if vlanId:
+                recipientComp.startFilter( pktFilter = ( "vlan {}".format( vlanId ) + " && " + packetFilter.format( senderComp.hostMac ) ) )
+            else:
+                recipientComp.startFilter( pktFilter = packetFilter.format( senderComp.hostMac ) )
 
             if not packet:
-                pkt = 'Ether( src="{0}", dst="{2}" )/IP( src="{1}", dst="{3}" )'.format(
-                    senderComp.hostMac,
-                    senderComp.hostIp,
-                    recipientComp.hostMac,
-                    recipientComp.hostIp )
+                if vlanId:
+                    pkt = 'Ether( src="{0}", dst="{2}" )/Dot1Q(vlan={4})/IP( src="{1}", dst="{3}" )'.format(
+                        senderComp.hostMac,
+                        senderComp.hostIp,
+                        recipientComp.hostMac,
+                        recipientComp.hostIp,
+                        vlanId )
+                else:
+                    pkt = 'Ether( src="{0}", dst="{2}" )/IP( src="{1}", dst="{3}" )'.format(
+                        senderComp.hostMac,
+                        senderComp.hostIp,
+                        recipientComp.hostMac,
+                        recipientComp.hostIp )
             else:
                 pkt = packet
-            senderComp.sendPacket( packet = pkt )
+            if vlanId:
+                senderComp.sendPacket( iface=( "{0}-eth0.{1}".format( sender, vlanId ) ), packet = pkt )
+            else:
+                senderComp.sendPacket( packet = pkt )
 
             if recipientComp.checkFilter( timeout ):
                 if expectFailure:
diff --git a/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.params b/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.params
index b3d3df3..d91559d 100644
--- a/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.params
+++ b/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.params
@@ -13,13 +13,14 @@
     # 16 - Balance ownership of switches
     # 17 - Activate Flow Objectives
     # 18 - Stop Mininet
+    # 19 - Copy karaf logs from ONOS nodes to TestON log directory
     # 1000 - Test host intents
     # 2000 - Test point intents
     # 3000 - Test single to multi point intents
     # 4000 - Test multi to single point intents
     # 5000 - Test host mobility
 
-    <testcases>1,[2,10,12,13,15,16,1000,2000,5000,18]*2,[2,10,12,13,15,16,17,1000,2000,5000,18]*2,[2,11,12,13,15,16,1000,2000,5000,18]*2,[2,11,12,13,15,16,17,1000,2000,5000,18]*2</testcases>
+    <testcases>1,[2,10,12,13,15,16,1000,2000,5000,18,19]*2,[2,10,12,13,15,16,17,1000,2000,5000,18,19]*2,[2,11,12,13,15,16,1000,2000,5000,18,19]*2,[2,11,12,13,15,16,17,1000,2000,5000,18,19]*2</testcases>
 
     <SCALE>
         <size>1,3,1,3,1,3,1,3</size>
diff --git a/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.py b/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.py
index f48ae3b..51ad5b1 100644
--- a/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.py
+++ b/TestON/tests/FUNC/FUNCintentRest/FUNCintentRest.py
@@ -70,6 +70,7 @@
             main.scapyHostNames = main.params[ 'SCAPY' ][ 'HOSTNAMES' ].split( ',' )
             main.scapyHosts = []  # List of scapy hosts for iterating
             main.assertReturnString = ''  # Assembled assert return string
+            main.cycle = 0 # How many times FUNCintent has run through its tests
 
             main.ONOSip = main.ONOSbench.getOnosIps()
             print main.ONOSip
@@ -152,6 +153,8 @@
         - Connect to cli
         """
 
+        main.cycle += 1
+
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.numCtrls = int( main.scale[ 0 ] )
         main.flowCompiler = "Flow Rules"
@@ -770,6 +773,39 @@
             main.cleanup()
             main.exit()
 
+    def CASE19( self, main ):
+        """
+            Copy the karaf.log files after each testcase cycle
+        """
+        main.log.report( "Copy karaf logs" )
+        main.case( "Copy karaf logs" )
+        main.caseExplanation = "Copying the karaf logs to preserve them through" +\
+                               "reinstalling ONOS"
+        main.step( "Copying karaf logs" )
+        stepResult = main.TRUE
+        scpResult = main.TRUE
+        copyResult = main.TRUE
+        i = 0
+        for cli in main.CLIs2:
+            main.node = cli
+            ip = main.ONOSip[ i ]
+            main.node.ip_address = ip
+            scpResult = scpResult and main.ONOSbench.scp( main.node ,
+                                            "/opt/onos/log/karaf.log",
+                                            "/tmp/karaf.log",
+                                            direction="from" )
+            copyResult = copyResult and main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,
+                                                                    copyFileName=( "karaf.log.node{0}.cycle{1}".format( str( i + 1 ), str( main.cycle ) ) ) )
+            if scpResult and copyResult:
+                stepResult =  main.TRUE and stepResult
+            else:
+                stepResult = main.FALSE and stepResult
+            i += 1
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully copied remote ONOS logs",
+                                 onfail="Failed to copy remote ONOS logs" )
+
     def CASE1000( self, main ):
         """
             Add host intents between 2 host:
@@ -941,8 +977,8 @@
 
         main.step( "VLAN1: Add vlan host intents between h4 and h12" )
         main.assertReturnString = "Assertion Result vlan IPV4\n"
-        host1 = { "name":"h4","id":"00:00:00:00:00:04/100" }
-        host2 = { "name":"h12","id":"00:00:00:00:00:0C/100" }
+        host1 = { "name":"h4","id":"00:00:00:00:00:04/100", "vlanId":"100" }
+        host2 = { "name":"h12","id":"00:00:00:00:00:0C/100", "vlanId":"100" }
         testResult = main.FALSE
         installResult = main.intentFunction.installHostIntent( main,
                                               name='VLAN1',
@@ -966,32 +1002,33 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
 
-        main.step( "VLAN2: Add inter vlan host intents between h13 and h20" )
-        main.assertReturnString = "Assertion Result different VLAN negative test\n"
-        host1 = { "name":"h13" }
-        host2 = { "name":"h20" }
-        testResult = main.FALSE
-        installResult = main.intentFunction.installHostIntent( main,
-                                              name='VLAN2',
-                                              onosNode='0',
-                                              host1=host1,
-                                              host2=host2 )
+        # This step isn't currently possible to perform in the REST API
+        # main.step( "VLAN2: Add inter vlan host intents between h13 and h20" )
+        # main.assertReturnString = "Assertion Result different VLAN negative test\n"
+        # host1 = { "name":"h13" }
+        # host2 = { "name":"h20" }
+        # testResult = main.FALSE
+        # installResult = main.intentFunction.installHostIntent( main,
+        #                                       name='VLAN2',
+        #                                       onosNode='0',
+        #                                       host1=host1,
+        #                                       host2=host2 )
 
-        if installResult:
-            testResult = main.intentFunction.testHostIntent( main,
-                                              name='VLAN2',
-                                              intentId = installResult,
-                                              onosNode='0',
-                                              host1=host1,
-                                              host2=host2,
-                                              sw1='s5',
-                                              sw2='s2',
-                                              expectedLink = 18 )
+        # if installResult:
+        #     testResult = main.intentFunction.testHostIntent( main,
+        #                                       name='VLAN2',
+        #                                       intentId = installResult,
+        #                                       onosNode='0',
+        #                                       host1=host1,
+        #                                       host2=host2,
+        #                                       sw1='s5',
+        #                                       sw2='s2',
+        #                                       expectedLink = 18 )
 
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=testResult,
-                                 onpass=main.assertReturnString,
-                                 onfail=main.assertReturnString )
+        # utilities.assert_equals( expect=main.TRUE,
+        #                          actual=testResult,
+        #                          onpass=main.assertReturnString,
+        #                          onfail=main.assertReturnString )
 
         # Change the following to use the REST API when leader checking is
         # supported by it
@@ -1194,7 +1231,8 @@
                                          recipients=recipients,
                                          sw1="s5",
                                          sw2="s2",
-                                         expectedLink=18)
+                                         expectedLink=18,
+                                         useTCP=True )
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=testResult,
@@ -1266,23 +1304,22 @@
         main.step( "VLAN: Add point intents between h5 and h21" )
         main.assertReturnString = "Assertion Result for VLAN IPV4 with mac address point intents\n"
         senders = [
-            { "name":"h5","device":"of:0000000000000005/5","mac":"00:00:00:00:00:05" }
+            { "name":"h5","device":"of:0000000000000005/5","mac":"00:00:00:00:00:05", "vlanId":"200" }
         ]
         recipients = [
-            { "name":"h21","device":"of:0000000000000007/5","mac":"00:00:00:00:00:15" }
+            { "name":"h21","device":"of:0000000000000007/5","mac":"00:00:00:00:00:15", "vlanId":"200" }
         ]
         installResult = main.intentFunction.installPointIntent(
                                        main,
-                                       name="DUALSTACK1",
+                                       name="VLAN",
                                        senders=senders,
-                                       recipients=recipients,
-                                       ethType="IPV4" )
+                                       recipients=recipients )
 
         if installResult:
             testResult = main.intentFunction.testPointIntent(
                                          main,
                                          intentId=installResult,
-                                         name="DUALSTACK1",
+                                         name="VLAN",
                                          senders=senders,
                                          recipients=recipients,
                                          sw1="s5",
@@ -1294,6 +1331,8 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
 
+        # TODO: implement VLAN selector REST API intent test once supported
+
         main.step( "1HOP: Add point intents between h1 and h3" )
         main.assertReturnString = "Assertion Result for 1HOP IPV4 with no mac address point intents\n"
         senders = [
diff --git a/TestON/tests/FUNC/FUNCintentRest/dependencies/FuncIntentFunction.py b/TestON/tests/FUNC/FUNCintentRest/dependencies/FuncIntentFunction.py
index 8a48a13..cefb077 100644
--- a/TestON/tests/FUNC/FUNCintentRest/dependencies/FuncIntentFunction.py
+++ b/TestON/tests/FUNC/FUNCintentRest/dependencies/FuncIntentFunction.py
@@ -75,12 +75,14 @@
         if not host2.get( "id" ):
             main.log.warn( "ID not given for host2 {0}. Loading from main.hostData".format( host2.get( "name" ) ) )
             host2[ "id" ] = main.hostsData.get( host2.get( "name" ) ).get( "id" )
+        vlanId = host1.get( "vlanId" )
 
         # Adding host intents
         main.log.info( itemName + ": Adding host intents" )
 
         intent1 = main.CLIs[ onosNode ].addHostIntent( hostIdOne=host1.get( "id" ),
-                                                       hostIdTwo=host2.get( "id" ) )
+                                                       hostIdTwo=host2.get( "id" ),
+                                                       vlanId=vlanId )
 
         # Get all intents ID in the system, time delay right after intents are added
         time.sleep( main.addIntentSleep )
@@ -175,6 +177,7 @@
 
         senderNames = [ host1.get( "name" ), host2.get( "name" ) ]
         recipientNames = [ host1.get( "name" ), host2.get( "name" ) ]
+        vlanId = host1.get( "vlanId" )
     except ( KeyError, TypeError ):
         main.log.error( "There was a problem loading the hosts data." )
         return main.FALSE
@@ -197,7 +200,7 @@
         testResult = main.FALSE
 
     # Check Connectivity
-    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
         main.assertReturnString += 'Initial Ping Passed\n'
     else:
         main.assertReturnString += 'Initial Ping Failed\n'
@@ -234,7 +237,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
             main.assertReturnString += 'Link Down Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Down Pingall Failed\n'
@@ -272,7 +275,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId ) ):
             main.assertReturnString += 'Link Up Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Up Pingall Failed\n'
@@ -357,6 +360,7 @@
             if not recipient.get( "device" ):
                 main.log.warn( "Device not given for recipient {0}. Loading from main.hostData".format( recipient.get( "name" ) ) )
                 recipient[ "device" ] = main.hostsData.get( recipient.get( "name" ) ).get( "location" )
+        vlanId = senders[ 0 ].get( "vlanId" )
 
 
         ingressDevice = senders[ 0 ].get( "device" )
@@ -387,7 +391,8 @@
                                             ipSrc=ipSrc,
                                             ipDst=ipDst,
                                             tcpSrc=tcpSrc,
-                                            tcpDst=tcpDst )
+                                            tcpDst=tcpDst,
+                                            vlanId=vlanId )
 
         time.sleep( main.addIntentSleep )
         intentsId = main.CLIs[ 0 ].getIntentsId()
@@ -422,7 +427,8 @@
                      tcp="",
                      sw1="s5",
                      sw2="s2",
-                     expectedLink=0):
+                     expectedLink=0,
+                     useTCP=False ):
     """
     Test a Point Intent
 
@@ -500,6 +506,7 @@
             if not recipient.get( "device" ):
                 main.log.warn( "Device not given for recipient {0}. Loading from main.hostData".format( recipient.get( "name" ) ) )
                 recipient[ "device" ] = main.hostsData.get( recipient.get( "name" ) ).get( "location" )
+        vlanId=senders[ 0 ].get( "vlanId" )
     except (KeyError, TypeError):
         main.log.error( "There was a problem loading the hosts data." )
         return main.FALSE
@@ -522,7 +529,7 @@
         testResult = main.FALSE
 
     # Check Connectivity
-    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+    if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames,  vlanId, useTCP ) ):
         main.assertReturnString += 'Initial Ping Passed\n'
     else:
         main.assertReturnString += 'Initial Ping Failed\n'
@@ -576,7 +583,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames, vlanId, useTCP  ) ):
             main.assertReturnString += 'Link Down Pingall Passed\n'
         else:
             main.assertReturnString += 'Link Down Pingall Failed\n'
@@ -614,7 +621,7 @@
             testResult = main.FALSE
 
         # Check Connection
-        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames ) ):
+        if utilities.retry( f=scapyCheckConnection, retValue=main.FALSE, args=( main, senderNames, recipientNames,  vlanId, useTCP  ) ):
             main.assertReturnString += 'Link Up Scapy Packet Received Passed\n'
         else:
             main.assertReturnString += 'Link Up Scapy Packet Recieved Failed\n'
@@ -1413,7 +1420,7 @@
 
     for i in range( main.numCtrls ):
         topologyResult = main.CLIs[ i ].topology()
-        statusResult = main.ONOSbench.checkStatus( topologyResult,
+        statusResult = main.CLIs[ i ].checkStatus( topologyResult,
                                                    main.numSwitch,
                                                    expectedLink )\
                        and statusResult
@@ -1616,7 +1623,7 @@
         main.log.error( "TypeError while populating hostsData" )
         return main.FALSE
 
-def scapyCheckConnection( main, senders, recipients, packet=None, packetFilter=None, expectFailure=False ):
+def scapyCheckConnection( main, senders, recipients, vlanId=None, useTCP=False, packet=None, packetFilter=None, expectFailure=False ):
     """
         Checks the connectivity between all given sender hosts and all given recipient hosts
         Packet may be specified. Defaults to Ether/IP packet
@@ -1630,7 +1637,8 @@
 
     if not packetFilter:
         packetFilter = 'ether host {}'
-
+    if useTCP:
+        packetFilter += ' ip proto \\tcp tcp port {}'.format(main.params[ 'SDNIP' ][ 'dstPort' ])
     if expectFailure:
         timeout = 1
     else:
@@ -1655,17 +1663,31 @@
                 connectionsFunctional = main.FALSE
                 continue
 
-            recipientComp.startFilter( pktFilter = packetFilter.format( senderComp.hostMac ) )
+            if vlanId:
+                recipientComp.startFilter( pktFilter = ( "vlan {}".format( vlanId ) + " && " + packetFilter.format( senderComp.hostMac ) ) )
+            else:
+                recipientComp.startFilter( pktFilter = packetFilter.format( senderComp.hostMac ) )
 
             if not packet:
-                pkt = 'Ether( src="{0}", dst="{2}" )/IP( src="{1}", dst="{3}" )'.format(
-                    senderComp.hostMac,
-                    senderComp.hostIp,
-                    recipientComp.hostMac,
-                    recipientComp.hostIp )
+                if vlanId:
+                    pkt = 'Ether( src="{0}", dst="{2}" )/Dot1Q(vlan={4})/IP( src="{1}", dst="{3}" )'.format(
+                        senderComp.hostMac,
+                        senderComp.hostIp,
+                        recipientComp.hostMac,
+                        recipientComp.hostIp,
+                        vlanId )
+                else:
+                    pkt = 'Ether( src="{0}", dst="{2}" )/IP( src="{1}", dst="{3}" )'.format(
+                        senderComp.hostMac,
+                        senderComp.hostIp,
+                        recipientComp.hostMac,
+                        recipientComp.hostIp )
             else:
                 pkt = packet
-            senderComp.sendPacket( packet = pkt )
+            if vlanId:
+                senderComp.sendPacket( iface=( "{0}-eth0.{1}".format( sender, vlanId ) ), packet = pkt )
+            else:
+                senderComp.sendPacket( packet = pkt )
 
             if recipientComp.checkFilter( timeout ):
                 if expectFailure:
diff --git a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.params b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.params
index 58917ba..73a3599 100644
--- a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.params
+++ b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.params
@@ -2,15 +2,19 @@
     # CASE - Description
     # 1 - Variable initialization and optional pull and build ONOS package
     # 2 - Install ONOS
-    # 11 - Start Mininet with Openflow 1.3 (OpenvSwitch --version more than 2.3.1)
+    # 11 - Start Mininet with Openflow 1.3 (Use OpenvSwitch version more than 2.3.1)
     # 12 - Assign switch to controller
+    # 13 - Discover hosts with Mininet Pingall(proto="IPV6")
     # 14 - Stop Mininet
+    # 16 - Balance ownership of switches
+    # 1000 - Test host intents
     # 2000 - Test Point Intent
     # 3000 - Test Single To Multi Point Intent
     # 4000 - Test multi to single point intents
     # 5000 - Test host mobility
+    # 6000 - Test Multi Point intent End Point Failure
 
-    <testcases>1,2,11,12,13,2000,3000,4000,5000,14</testcases>
+    <testcases>1,2,11,12,13,16,1000,2000,3000,4000,5000,6000,14</testcases>
 
     <SCALE>
         <size>1</size>
diff --git a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.py b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.py
old mode 100644
new mode 100755
index 9c49283..450516a
--- a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.py
+++ b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.py
@@ -346,6 +346,21 @@
                                 onpass="Successfully discovered hosts",
                                 onfail="Failed to discover hosts" )
 
+    def CASE16( self, main ):
+        """
+            Balance Masters
+        """
+        main.case( "Balance mastership of switches" )
+        main.step( "Balancing mastership of switches" )
+
+        balanceResult = main.FALSE
+        balanceResult = utilities.retry( f=main.CLIs[ 0 ].balanceMasters, retValue=main.FALSE, args=[] )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=balanceResult,
+                                 onpass="Successfully balanced mastership of switches",
+                                 onfail="Failed to balance mastership of switches" )
+
     def CASE14( self, main ):
         """
             Stop mininet
@@ -367,6 +382,130 @@
             main.cleanup()
             main.exit()
 
+    def CASE1000( self, main ):
+        """
+            Add host intents between 2 host:
+                - Discover hosts
+                - Add host intents
+                - Check intents
+                - Verify flows
+                - Ping hosts
+                - Reroute
+                    - Link down
+                    - Verify flows
+                    - Check topology
+                    - Ping hosts
+                    - Link up
+                    - Verify flows
+                    - Check topology
+                    - Ping hosts
+                - Remove intents
+        """
+        import time
+        import json
+        import re
+
+        # Assert variables - These variable's name|format must be followed
+        # if you want to use the wrapper function
+        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"
+
+        intentLeadersOld = main.CLIs[ 0 ].leaderCandidates()
+
+        main.testName = "Host Intents"
+        main.case( main.testName + " 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 IPV6, Dual stack, VLAN " +\
+                                "etc;\nThe test will use OF " + main.OFProtocol\
+                                + " OVS running in Mininet"
+
+        main.step( "IPV6: Add host intents between h1 and h9" )
+        stepResult = main.TRUE
+        stepResult = main.intentFunction.hostIntent( main,
+                                              name='IPV6',
+                                              host1='h1',
+                                              host2='h9',
+                                              host1Id='00:00:00:00:00:01/-1',
+                                              host2Id='00:00:00:00:00:09/-1',
+                                              sw1='s5',
+                                              sw2='s2',
+                                              expectedLink=18 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="IPV6: Host intent test successful " +
+                                        "between two IPV6 hosts",
+                                 onfail="IPV6: Host intent test failed " +
+                                        "between two IPV6 hosts")
+
+        main.step( "DUALSTACK1: Add host intents between h3 and h11" )
+        stepResult = main.TRUE
+        stepResult = main.intentFunction.hostIntent( main,
+                                              name='DUALSTACK',
+                                              host1='h3',
+                                              host2='h11',
+                                              host1Id='00:00:00:00:00:03/-1',
+                                              host2Id='00:00:00:00:00:0B/-1',
+                                              sw1='s5',
+                                              sw2='s2',
+                                              expectedLink=18 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="DUALSTACK: Host intent test " +
+                                        "successful between two " +
+                                        "dual stack host using IPV6",
+                                 onfail="DUALSTACK: Host intent test " +
+                                        "failed between two" +
+                                        "dual stack host using IPV6" )
+
+        main.step( "1HOP: Add host intents between h1 and h3" )
+        stepResult = main.TRUE
+        stepResult = main.intentFunction.hostIntent( main,
+                                              name='1HOP',
+                                              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="1HOP: Host intent test " +
+                                        "successful between two " +
+                                        "host using IPV6 in the same switch",
+                                 onfail="1HOP: Host intent test " +
+                                        "failed between two" +
+                                        "host using IPV6 in the same switch" )
+
+        main.step( "VLAN: Add vlan host intents between h5 and h24" )
+        stepResult = main.TRUE
+        stepResult = main.intentFunction.hostIntent( main,
+                                              name='VLAN1',
+                                              host1='h5',
+                                              host2='h24',
+                                              host1Id='00:00:00:00:00:05/100',
+                                              host2Id='00:00:00:00:00:18/100',
+                                              sw1='s5',
+                                              sw2='s2',
+                                              expectedLink=18 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="VLAN: Host intent test " +
+                                        "successful between two " +
+                                        "host using IPV6 in the same VLAN",
+                                 onfail="VLAN1: Host intent test " +
+                                        "failed between two" +
+                                        "host using IPV6 in the same VLAN" )
+
+        main.intentFunction.report( main )
+
     def CASE2000( self, main ):
         """
             add point intents between 2 hosts:
@@ -586,12 +725,12 @@
                                  onfail=main.assertReturnString )
         main.step( "1HOP: Add point intents between h1 and h9" )
         main.assertReturnString = "Assertion Result for 1HOP IPV6 with no mac address point intents\n"
-        stepResult = main.intentFunction.hostIntent( main,
+        stepResult = main.intentFunction.pointIntent( main,
                                               name='1HOP',
-                                              host1='h1',
-                                              host2='h9',
-                                              host1Id='00:00:00:00:00:01/-1',
-                                              host2Id='00:00:00:00:00:09/-1')
+                                              host1="h1",
+                                              host2="h9",
+                                              deviceId1="of:0000000000000005/1",
+                                              deviceId2="of:0000000000000006/1")
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass=main.assertReturnString,
@@ -863,3 +1002,221 @@
                                  onpass=main.assertReturnString,
                                  onfail=main.assertReturnString )
         main.intentFunction.report( main )
+
+    def CASE6000( self, main ):
+        """
+        Tests Multi to Single Point Intent and Single to Multi Point Intent End Point Failure
+        """
+        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 Multi to Single End Point Failure" )
+        main.step( "NOOPTION: test end point failure for multi point to single point intents" )
+        main.assertReturnString = "Assertion results for IPV6 multi to single \
+                                  point intent end point failure with no options set\n"
+        hostNames = [ 'h8', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000007/1' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         name="NOOPTION",
+                                         test="MultipletoSingle",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "IPV6: test end point failure for multi point to single point intents" )
+        main.assertReturnString = "Assertion results for IPV6 multi to single \
+                                  point intent end point failure with IPV6 type and MAC addresses\n"
+        hostNames = [ 'h8', 'h9', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000006/1', 'of:0000000000000007/1' ]
+        macs = [ '00:00:00:00:00:08','00:00:00:00:00:09' ,'00:00:00:00:00:11' ]        
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="MultipletoSingle",
+                                         name="IPV6",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         macs=macs,
+                                         ethType="IPV6",
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "IPV6_2: test end point faliure for multi point to single point intents" )
+        main.assertReturnString = "Assertion results for IPV6 multi to single \
+                                  point intent end point failure with IPV6 type and no MAC addresses\n"
+        hostNames = [ 'h8', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000007/1' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="MultipletoSingle",
+                                         name="IPV6_2",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         ethType="IPV6",
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "VLAN: test end point failure for multi point to single point intents" )
+        main.assertReturnString = "Assertion results for IPV6 multi to single \
+                                  point intent end point failure with IPV6 type and no MAC addresses in the same VLAN\n"
+        hostNames = [ 'h5', 'h24' ]
+        devices = [ 'of:0000000000000005/5', 'of:0000000000000007/8' ]
+        macs = [ '00:00:00:00:00:05','00:00:00:00:00:18' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="MultipletoSingle",
+                                         name="VLAN",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         ethType="IPV6",
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.case( "Test Single to Multiple End Point Failure" )
+        main.step( "NOOPTION: test end point failure for single point to multi point intents" )
+        main.assertReturnString = "Assertion results for IPV6 single to multi \
+                                  point intent end point failure with no options set\n"
+        hostNames = [ 'h8', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000007/1' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="SingletoMultiple",
+                                         name="NOOPTION",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+        main.step( "IPV6: test end point failure for single point to multi point intents" )
+        main.assertReturnString = "Assertion results for IPV6 single to multi \
+                                  point intent end point failure with IPV6 type and MAC addresses\n"
+        hostNames = [ 'h8', 'h9', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000006/1', 'of:0000000000000007/1' ]
+        macs = [ '00:00:00:00:00:08','00:00:00:00:00:09' ,'00:00:00:00:00:11' ]  
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="SingletoMultiple",
+                                         name="IPV6",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         ethType="IPV6",
+                                         macs=macs,
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "IPV6_2: test end point failure for single point to multi point intents" )
+        main.assertReturnString = "Assertion results for IPV6 single to multi\
+                                  point intent endpoint failure with IPV6 type and no MAC addresses\n"
+        hostNames = [ 'h8', 'h17' ]
+        devices = [ 'of:0000000000000005/8', 'of:0000000000000007/1' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="SingletoMultiple",
+                                         name="IPV6_2",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         ethType="IPV6",
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.step( "VLAN: test end point failure for single point to multi point intents" )
+        main.assertReturnString = "Assertion results for IPV6 single to multi point\
+                                  intent endpoint failure with IPV6 type and MAC addresses in the same VLAN\n"
+        hostNames = [ 'h5', 'h24' ]
+        devices = [ 'of:0000000000000005/5', 'of:0000000000000007/8' ]
+        macs = [ '00:00:00:00:00:05','00:00:00:00:00:18' ]
+        testResult = main.TRUE
+        testResult = main.intentFunction.testEndPointFail(
+                                         main,
+                                         test="SingletoMultiple",
+                                         name="IPV6",
+                                         hostNames=hostNames,
+                                         devices=devices,
+                                         macs=macs,
+                                         ethType="IPV6",
+                                         sw1="s6",
+                                         sw2="s2",
+                                         sw3="s4",
+                                         sw4="s1",
+                                         sw5="s3",
+                                         expectedLink1=16,
+                                         expectedLink2=14 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=testResult,
+                                 onpass=main.assertReturnString,
+                                 onfail=main.assertReturnString )
+
+        main.intentFunction.report( main )
diff --git a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.topo b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.topo
index dd25a2e..3e9369f 100755
--- a/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.topo
+++ b/TestON/tests/FUNC/FUNCipv6Intent/FUNCipv6Intent.topo
@@ -78,4 +78,4 @@
         </Mininet1>
 
     </COMPONENT>
-</TOPOLOGY>
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/FUNC/FUNCipv6Intent/dependencies/FUNCIpv6IntentFunction.py b/TestON/tests/FUNC/FUNCipv6Intent/dependencies/FUNCIpv6IntentFunction.py
index b6d706e..40a125b 100644
--- a/TestON/tests/FUNC/FUNCipv6Intent/dependencies/FUNCIpv6IntentFunction.py
+++ b/TestON/tests/FUNC/FUNCipv6Intent/dependencies/FUNCIpv6IntentFunction.py
@@ -133,11 +133,23 @@
     # Check intents state again if first check fails...
     if not intentResult:
         intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
 
     # Check flows count in each node
-    checkFlowsCount( main )
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
     # Verify flows
-    checkFlowsState( main )
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
 
     # Ping hosts
     firstPingResult = main.Mininet1.ping6pair(SRC=hostNames[0], TARGET=main.hostsData[ host2 ][ 'ipAddresses' ][ 0 ])
@@ -391,11 +403,23 @@
     # Check intents state again if first check fails...
     if not intentResult:
         intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
 
     # Check flows count in each node
-    checkFlowsCount( main )
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
     # Verify flows
-    checkFlowsState( main )
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
 
     # Ping hosts
     pingTemp = ping6allHosts( main, hostNames )
@@ -671,14 +695,25 @@
     # Check intents state again if first check fails...
     if not intentResult:
         intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
 
     # Check flows count in each node
-    checkFlowsCount( main )
-
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
     # Verify flows
-    checkFlowsState( main )
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
 
-    # Run iperf to both host
+        # Run iperf to both host
     iperfTemp = main.Mininet1.iperftcpipv6( host1,host2 )
     iperfResult = iperfResult and iperfTemp
     if iperfTemp:
@@ -893,7 +928,7 @@
                            main.hostsData.get( host ).get( 'mac' )
                ipDict[ main.hostsData.get( host ).get( 'location' ) ] = \
                            main.hostsData.get( host ).get( 'ipAddresses' )
-    
+
     pingResult = main.TRUE
     intentResult = main.TRUE
     removeIntentResult = main.TRUE
@@ -946,7 +981,7 @@
                                             tcpSrc="",
                                             tcpDst="" ) )
 
-    
+
     # Check intents state
     time.sleep( main.checkIntentSleep )
     intentResult = checkIntentState( main, intentsId )
@@ -954,11 +989,23 @@
     # Check intents state again if first check fails...
     if not intentResult:
         intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
 
     # Check flows count in each node
-    checkFlowsCount( main )
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
     # Verify flows
-    checkFlowsState( main )
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
 
     firstPingResult = main.Mininet1.ping6pair(SRC=hostNames[0], TARGET=main.hostsData[ hostNames[1] ][ 'ipAddresses' ][0])
     if not firstPingResult:
@@ -1140,6 +1187,7 @@
         expectedLink - Expected link when the switches are down, it should
                        be two links lower than the links before the two
                        switches are down
+        Note - Don't use more than 2 hosts for MultiToSingle Intent with no mac address option.
     """
 
     assert main, "There is no main variable"
@@ -1236,11 +1284,23 @@
     # Check intents state again if first check fails...
     if not intentResult:
         intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
 
     # Check flows count in each node
-    checkFlowsCount( main )
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
     # Verify flows
-    checkFlowsState( main )
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
 
     # Ping hosts...
     pingTemp = ping6allHosts( main, hostNames )
@@ -1349,6 +1409,376 @@
 
     return stepResult
 
+def testEndPointFail( main,
+                      name,
+                      test="",
+                      hostNames="",
+                      devices="",
+                      macs="",
+                      ports="",
+                      onosNode=0,
+                      ethType="",
+                      bandwidth="",
+                      lambdaAlloc=False,
+                      ipProto="",
+                      ipAddresses="",
+                      tcp="",
+                      sw1="",
+                      sw2="",
+                      sw3="",
+                      sw4="",
+                      sw5="",
+                      expectedLink1=0,
+                      expectedLink2=0 ):
+    """
+    Test Multipoint Topology for Endpoint failures
+    """
+
+    assert main, "There is no main variable"
+    assert hostNames, "You must specify hosts"
+    assert devices or main.hostsData, "You must specify devices"
+
+    global itemName
+    itemName = name
+    tempHostsData = {}
+    intentsId = []
+    onosNode = int( onosNode )
+
+    macsDict = {}
+    ipDict = {}
+    if hostNames and devices:
+        if len( hostNames ) != len( devices ):
+            main.log.debug( "hosts and devices does not have the same length" )
+            return main.FALSE
+        if ports:
+            if len( ports ) != len( devices ):
+                main.log.error( "Ports and devices does " +
+                                "not have the same length" )
+                return main.FALSE
+        else:
+            main.log.info( "Device Ports are not specified" )
+        if macs:
+            for i in range( len( devices ) ):
+                macsDict[ devices[ i ] ] = macs[ i ]
+    elif hostNames and not devices and main.hostsData:
+        devices = []
+        main.log.info( "multiIntent function is using main.hostsData" )
+        for host in hostNames:
+               devices.append( main.hostsData.get( host ).get( 'location' ) )
+               macsDict[ main.hostsData.get( host ).get( 'location' ) ] = \
+                           main.hostsData.get( host ).get( 'mac' )
+               ipDict[ main.hostsData.get( host ).get( 'location' ) ] = \
+                           main.hostsData.get( host ).get( 'ipAddresses' )
+
+    pingResult = main.TRUE
+    intentResult = main.TRUE
+    removeIntentResult = main.TRUE
+    flowResult = main.TRUE
+    topoResult = main.TRUE
+    linkDownResult = main.TRUE
+    linkUpResult = main.TRUE
+
+    devicesCopy = copy.copy( devices )
+    if ports:
+        portsCopy = copy.copy( ports )
+    main.log.info( itemName + ": Adding intents" )
+
+    # Check flows count in each node
+    checkFlowsCount( main )
+
+    if test=="MultipletoSingle":
+        for i in range( len( devices ) ):
+            egressDevice = devicesCopy[ i ]
+            ingressDeviceList = copy.copy( devicesCopy )
+            ingressDeviceList.remove( egressDevice )
+            if ports:
+                portEgress = portsCopy[ i ]
+                portIngressList = copy.copy( portsCopy )
+                del portIngressList[ i ]
+            else:
+                portEgress = ""
+                portIngressList = None
+            if not macsDict:
+                dstMac = ""
+            else:
+                dstMac = macsDict[ egressDevice ]
+                if dstMac == None:
+                    main.log.debug( "There is no MAC in device - " + egressDevice )
+                    dstMac = ""
+
+            intentsId.append(
+                            main.CLIs[ onosNode ].addMultipointToSinglepointIntent(
+                                                ingressDeviceList=ingressDeviceList,
+                                                egressDevice=egressDevice,
+                                                portIngressList=portIngressList,
+                                                portEgress=portEgress,
+                                                ethType=ethType,
+                                                ethDst=dstMac,
+                                                bandwidth=bandwidth,
+                                                lambdaAlloc=lambdaAlloc,
+                                                ipProto=ipProto,
+                                                ipSrc="",
+                                                ipDst="",
+                                                tcpSrc="",
+                                                tcpDst="" ) )
+
+
+    elif test=="SingletoMultiple":
+        for i in range( len( devices ) ):
+            ingressDevice = devicesCopy[ i ]
+            egressDeviceList = copy.copy( devicesCopy )
+            egressDeviceList.remove( ingressDevice )
+            if ports:
+                portIngress = portsCopy[ i ]
+                portEgressList = copy.copy( portsCopy )
+                del portEgressList[ i ]
+            else:
+                portIngress = ""
+                portEgressList = None
+            if not macsDict:
+                srcMac = ""
+            else:
+                srcMac = macsDict[ ingressDevice ]
+                if srcMac == None:
+                    main.log.debug( "There is no MAC in device - " + ingressDevice )
+                    srcMac = ""
+
+            intentsId.append(
+                            main.CLIs[ onosNode ].addSinglepointToMultipointIntent(
+                                                ingressDevice=ingressDevice,
+                                                egressDeviceList=egressDeviceList,
+                                                portIngress=portIngress,
+                                                portEgressList=portEgressList,
+                                                ethType=ethType,
+                                                ethSrc=srcMac,
+                                                bandwidth=bandwidth,
+                                                lambdaAlloc=lambdaAlloc,
+                                                ipProto=ipProto,
+                                                ipSrc="",
+                                                ipDst="",
+                                                tcpSrc="",
+                                                tcpDst="" ) )
+
+    else:
+        main.log.info("Invalid test Name - Type either SingletoMultiple or MultipletoSingle")
+        return main.FALSE
+
+    # Check intents state
+    time.sleep( main.checkIntentSleep )
+    intentResult = checkIntentState( main, intentsId )
+
+    # Check intents state again if first check fails...
+    if not intentResult:
+        intentResult = checkIntentState( main, intentsId )
+        if intentResult:
+            main.assertReturnString += 'Initial Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Initial Intent State Failed\n'
+
+    # Check flows count in each node
+    FlowResult = checkFlowsCount( main )
+    if FlowResult:
+        main.assertReturnString += 'Initial Flow Count Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow Count Failed\n'
+    # Verify flows
+    StateResult = checkFlowsState( main )
+    if StateResult:
+        main.assertReturnString += 'Initial Flow State Passed\n'
+    else:
+        main.assertReturnString += 'Initial Flow State Failed\n'
+
+    # Ping hosts...
+    pingTemp = ping6allHosts( main, hostNames )
+    pingResult = pingResult and pingTemp
+    if pingTemp:
+        main.assertReturnString += 'Initial Pingall Passed\n'
+    else:
+        main.assertReturnString += 'Initial Pingall Failed\n'
+
+    # Test rerouting if these variables exist
+    if sw1 and sw2 and sw3 and sw4 and sw5 and expectedLink1 and expectedLink2:
+    # Take two links down
+        # Take first link down
+        linkDownResult1 = link( main, sw1, sw2, "down" )
+        if linkDownResult1:
+            main.assertReturnString += 'First Link Down Passed\n'
+        else:
+            main.assertReturnString += 'First Link Down Failed\n'
+
+        # Take second link down
+        linkDownResult2 = link( main, sw3, sw4, "down" )
+        if linkDownResult2:
+            main.assertReturnString += 'Second Link Down Passed\n'
+        else:
+            main.assertReturnString += 'Second Link Down Failed\n'
+
+        # Check flows count in each node
+        FlowResult = checkFlowsCount( main )
+        if FlowResult:
+            main.assertReturnString += 'Link Down Flow Count Passed\n'
+        else:
+            main.assertReturnString += 'Link Down Flow Count Failed\n'
+        # Verify flows
+        StateResult = checkFlowsState( main )
+        if StateResult:
+            main.assertReturnString += 'Link Down Flow State Passed\n'
+        else:
+            main.assertReturnString += 'Link Down Flow State Failed\n'
+
+        # Check OnosTopology
+        topoResult = checkTopology( main, expectedLink1 )
+        if topoResult:
+            main.assertReturnString += 'Link Down Topology State Passed\n'
+        else:
+            main.assertReturnString += 'Link Down Topology State Failed\n'
+
+        # Ping hosts
+        pingTemp = ping6allHosts( main, hostNames )
+        pingResult = pingResult and pingTemp
+        if pingTemp:
+            main.assertReturnString += 'Link Down Ping6all Passed\n'
+        else:
+            main.assertReturnString += 'Link Down Ping6all Failed\n'
+
+        # Check intent state
+        intentTemp = checkIntentState( main, intentsId )
+        intentResult = intentResult and intentTemp
+        if intentTemp:
+            main.assertReturnString += 'Link Down Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Link Down Intent State Failed\n'
+
+        # Take third link down to isolate the node
+        linkDownResult3 = link( main, sw3, sw5, "down" )
+        if linkDownResult3:
+            main.assertReturnString += 'Isolation Third Link Down Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Third Link Down Failed\n'
+
+        # Check flows count in each node
+        FlowResult = checkFlowsCount( main )
+        if FlowResult:
+            main.assertReturnString += 'Isolation Link Down Flow Count Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Link Down Flow Count Failed\n'
+
+        # Verify flows
+        StateResult = checkFlowsState( main )
+        if StateResult:
+            main.assertReturnString += 'Isolation Link Down Flow State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Link Down Flow State Failed\n'
+
+        # Check OnosTopology
+        topoResult = checkTopology( main, expectedLink2 )
+        if topoResult:
+            main.assertReturnString += 'Isolation Link Down Topology State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Link Down Topology State Failed\n'
+
+        # Ping hosts after isolation
+        main.log.info("Ping will fail if the node is isolated correctly.It will ping only after bringing up the isolation link")
+        pingIsolation = ping6allHosts( main, hostNames )
+        if pingIsolation:
+            main.assertReturnString += 'Isolation Link Down Ping6all Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Link Down Ping6all Failed\n'
+
+        # Check intent state after isolation
+        main.log.info("Intent will be in FAILED state if the node is isolated correctly.It will be in INSTALLED state only after bringing up isolation link")
+        intentIsolation = checkIntentState( main, intentsId )
+        if intentIsolation:
+            main.assertReturnString += 'Isolation Link Down Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Isolation Link Down Intent State Failed\n'
+
+        linkDownResult = linkDownResult1 and linkDownResult2 and linkDownResult3
+
+        # Checks ONOS state in link down
+        if linkDownResult and topoResult and pingResult and intentResult:
+            main.log.info( itemName + ": Successfully brought link down" )
+        else:
+            main.log.error( itemName + ": Failed to bring link down" )
+
+        # Bring the links back up
+        # Bring first link up
+        linkUpResult1 = link( main, sw1, sw2, "up" )
+        if linkUpResult1:
+            main.assertReturnString += 'First Link Up Passed\n'
+        else:
+            main.assertReturnString += 'First Link Up Failed\n'
+
+        # Bring second link up
+        linkUpResult2 = link( main, sw3, sw4, "up" )
+        if linkUpResult2:
+            main.assertReturnString += 'Second Link Up Passed\n'
+        else:
+            main.assertReturnString += 'Second Link Up Failed\n'
+        # Bring third link up
+        linkUpResult3 = link( main, sw3, sw5, "up" )
+        if linkUpResult3:
+            main.assertReturnString += 'Third Link Up Passed\n'
+        else:
+            main.assertReturnString += 'Third Link Up Failed\n'
+
+        linkUpResult = linkUpResult1 and linkUpResult2 and linkUpResult3
+        time.sleep( main.rerouteSleep )
+
+        # Check flows count in each node
+        FlowResult = checkFlowsCount( main )
+        if FlowResult:
+            main.assertReturnString += 'Link Up Flow Count Passed\n'
+        else:
+            main.assertReturnString += 'Link Up Flow Count Failed\n'
+        # Verify flows
+        StateResult = checkFlowsState( main )
+        if StateResult:
+            main.assertReturnString += 'Link Up Flow State Passed\n'
+        else:
+            main.assertReturnString += 'Link Up Flow State Failed\n'
+
+        # Check OnosTopology
+        topoResult = checkTopology( main, main.numLinks )
+        if topoResult:
+            main.assertReturnString += 'Link Up Topology State Passed\n'
+        else:
+            main.assertReturnString += 'Link Up Topology State Failed\n'
+
+        # Ping hosts
+        pingTemp = ping6allHosts( main, hostNames )
+        pingResult = pingResult and pingTemp
+        if pingTemp:
+            main.assertReturnString += 'Link Up Pingall Passed\n'
+        else:
+            main.assertReturnString += 'Link Up Pingall Failed\n'
+
+        # Check Intents
+        intentTemp = checkIntentState( main, intentsId )
+        intentResult = intentResult and intentTemp
+        if intentTemp:
+            main.assertReturnString += 'Link Up Intent State Passed\n'
+        else:
+            main.assertReturnString += 'Link Up Intent State Failed\n'
+
+        # Checks ONOS state in link up
+        if linkUpResult and topoResult and pingResult and intentResult:
+            main.log.info( itemName + ": Successfully brought link back up" )
+        else:
+            main.log.error( itemName + ": Failed to bring link back up" )
+
+    # Remove all intents
+    removeIntentResult = removeAllIntents( main, intentsId )
+    if removeIntentResult:
+        main.assertReturnString += 'Remove Intents Passed'
+    else:
+        main.assertReturnString += 'Remove Intents Failed'
+
+    testResult = pingResult and linkDownResult and linkUpResult \
+                 and intentResult and removeIntentResult
+
+    return testResult
+
 def ping6allHosts( main, hostList ):
     # Ping all host in the hosts list variable
     main.log.info( "Pinging: " + str( hostList ) )
@@ -1415,7 +1845,7 @@
 
     for i in range( main.numCtrls ):
         topologyResult = main.CLIs[ i ].topology()
-        statusResult = main.ONOSbench.checkStatus( topologyResult,
+        statusResult = main.CLIs[ i ].checkStatus( topologyResult,
                                                    main.numSwitch,
                                                    expectedLink )\
                        and statusResult
diff --git a/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.params b/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.params
index bf5fbac..373f007 100644
--- a/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.params
+++ b/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.params
@@ -12,8 +12,10 @@
     # 22 - Add NetCfgs for discovered devices
     # 23 - Check NetCfgs after all devices are connected and NetCfgs are set
     # 24 - Remove NetCfgs
+    # 25 - Move network-cfg.json to onos directory for prebuild configurations
+    # 26 - Check that prebuild configurations are correct
 
-    <testcases>1,2,20,11,21,22,23,24</testcases>
+    <testcases>1,25,2,20,11,26,21,22,23,24</testcases>
 
     <DEPENDENCY>
         <path>/tests/FUNC/FUNCnetCfg/dependencies/</path>
@@ -36,7 +38,7 @@
     </SLEEP>
 
     <MININET>
-        <switch>4</switch>
+        <switch>6</switch>
     </MININET>
 
 </PARAMS>
diff --git a/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.py b/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.py
index 34bc72b..0f8812a 100644
--- a/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.py
+++ b/TestON/tests/FUNC/FUNCnetCfg/FUNCnetCfg.py
@@ -87,7 +87,7 @@
                 main.log.error( "Did not properly created list of ONOS handle" )
                 stepResult = main.FALSE
         except Exception as e:
-            main.log.exception(e)
+            main.log.exception( e )
             main.cleanup()
             main.exit()
 
@@ -194,19 +194,18 @@
         onosIsUp = main.TRUE
 
         for i in range( main.numCtrls ):
-            onosIsUp = onosIsUp and main.ONOSbench.isup( main.ONOSip[ i ] )
-        if onosIsUp == main.TRUE:
-            main.log.report( "ONOS instance is up and ready" )
-        else:
-            main.log.report( "ONOS instance may not be up, stop and " +
-                             "start ONOS again " )
-            for i in range( main.numCtrls ):
+            isUp = main.ONOSbench.isup( main.ONOSip[ i ] )
+            onosIsUp = onosIsUp and isUp
+            if isUp == main.TRUE:
+                main.log.report( "ONOS instance {0} is up and ready".format( i + 1 ) )
+            else:
+                main.log.report( "ONOS instance {0} may not be up, stop and ".format( i + 1 ) +
+                                 "start ONOS again " )
                 stopResult = stopResult and \
                         main.ONOSbench.onosStop( main.ONOSip[ i ] )
-            for i in range( main.numCtrls ):
                 startResult = startResult and \
                         main.ONOSbench.onosStart( main.ONOSip[ i ] )
-        stepResult = onosIsUp and stopResult and startResult
+            stepResult = onosIsUp and stopResult and startResult
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="ONOS service is ready",
@@ -334,7 +333,7 @@
 
         main.step( "Starting Mininet topology with OF 1.3 switches" )
         args = "--controller none --switch ovs,protocols=OpenFlow13"
-        switches = int( main.params['MININET']['switch'] )
+        switches = int( main.params[ 'MININET' ][ 'switch' ] )
         cmd = "mn --topo linear,{} {}".format( switches, args )
         topoResult = main.Mininet1.startNet( mnCmd = cmd )
         stepResult = topoResult
@@ -486,12 +485,15 @@
         devices = main.ONOSrest1.devices()
         main.log.debug( main.ONOSrest1.pprint( devices ) )
         allowedDevices = [ "of:{}".format( str( i ).zfill( 16 ) ) for i in [ 1, 2, 4 ] ]
-        print allowedDevices
+        main.log.debug( allowedDevices )
         onosDevices = []
-        for sw in json.loads( devices ):
-            onosDevices.append( str( sw['id'] ) )
-        onosDevices.sort()
-        print onosDevices
+        try:
+            for sw in json.loads( devices ):
+                onosDevices.append( str( sw['id'] ) )
+            onosDevices.sort()
+            main.log.debug( onosDevices )
+        except( TypeError, ValueError ):
+            main.log.error( "Problem loading devices" )
         utilities.assert_equals( expect=allowedDevices,
                                  actual=onosDevices,
                                  onpass="Only allowed devices are in ONOS",
@@ -500,15 +502,19 @@
 
         main.step( "Check device annotations" )
         keys = [ 'name', 'owner', 'rackAddress' ]
-        for sw in json.loads( devices ):
-            if "of:0000000000000001" in sw['id']:
-                s1Correct = True
-                for k in keys:
-                    if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s1Json[k] ):
-                        s1Correct = False
-                        main.log.debug( "{} is wrong on s1".format( k ) )
-                if not s1Correct:
-                    main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
+        try:
+            for sw in json.loads( devices ):
+                if "of:0000000000000001" in sw['id']:
+                    s1Correct = True
+                    for k in keys:
+                        if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s1Json[k] ):
+                            s1Correct = False
+                            main.log.debug( "{} is wrong on s1".format( k ) )
+                    if not s1Correct:
+                        main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
+        except( TypeError, ValueError ):
+            main.log.error( "Problem loading devices" )
+            s1Correct = False
         try:
             stepResult = s1Correct
         except NameError:
@@ -611,12 +617,15 @@
         main.log.debug( main.ONOSrest1.pprint( devices ) )
         allowedDevices = [ "of:{}".format( str( i ).zfill( 16 ) ) for i in [ 1, 2 ] ]
         onosDevices = []
-        for sw in json.loads( devices ):
-            onosDevices.append( str( sw.get( 'id' ) ) )
-        onosDevices.sort()
-        failMsg = "ONOS devices doesn't match the list of allowed devices.\n"
-        failMsg += "Expected devices: {}\nActual devices: {}".format( allowedDevices,
-                                                                      onosDevices )
+        try:
+            for sw in json.loads( devices ):
+                onosDevices.append( str( sw.get( 'id' ) ) )
+            onosDevices.sort()
+            failMsg = "ONOS devices doesn't match the list of allowed devices.\n"
+            failMsg += "Expected devices: {}\nActual devices: {}".format( allowedDevices,
+                                                                          onosDevices )
+        except( TypeError, ValueError ):
+            main.log.error( "Problem loading devices" )
         utilities.assert_equals( expect=allowedDevices,
                                  actual=onosDevices,
                                  onpass="Only allowed devices are in ONOS",
@@ -624,23 +633,27 @@
 
         main.step( "Check device annotations" )
         keys = [ 'name', 'owner', 'rackAddress' ]
-        for sw in json.loads( devices ):
-            if "of:0000000000000001" in sw.get( 'id' ):
-                s1Correct = True
-                for k in keys:
-                    if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s1Json[k] ):
-                        s1Correct = False
-                        main.log.debug( "{} is wrong on s1".format( k ) )
-                if not s1Correct:
-                    main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
-            elif "of:0000000000000002" in sw['id']:
-                s2Correct = True
-                for k in keys:
-                    if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s2Json[k] ):
-                        s2Correct = False
-                        main.log.debug( "{} is wrong on s2".format( k ) )
-                if not s2Correct:
-                    main.log.error( "Annotations for s2 are incorrect: {}".format( sw ) )
+        try:
+            for sw in json.loads( devices ):
+                if "of:0000000000000001" in sw.get( 'id' ):
+                    s1Correct = True
+                    for k in keys:
+                        if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s1Json[k] ):
+                            s1Correct = False
+                            main.log.debug( "{} is wrong on s1".format( k ) )
+                    if not s1Correct:
+                        main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
+                elif "of:0000000000000002" in sw['id']:
+                    s2Correct = True
+                    for k in keys:
+                        if str( sw.get( 'annotations', {} ).get( k ) ) != str( main.s2Json[k] ):
+                            s2Correct = False
+                            main.log.debug( "{} is wrong on s2".format( k ) )
+                    if not s2Correct:
+                        main.log.error( "Annotations for s2 are incorrect: {}".format( sw ) )
+        except( TypeError, ValueError ):
+            main.log.error( "Problem loading devices" )
+            stepResult = False
         try:
             stepResult = s1Correct and s2Correct
         except NameError:
@@ -758,3 +771,59 @@
                                  actual=get,
                                  onpass="Successfully removed device config",
                                  onfail="Failed to remove device config" )
+
+    def CASE25( self, main ):
+        """
+            Use network-cfg.json to configure devices during ONOS startup
+        """
+        main.case( "Preparing network-cfg.json to load configurations" )
+        main.step( "Moving network-cfg.json to $ONOS_ROOT/tools/package/config/" )
+        prestartResult = main.TRUE
+        srcPath = "~/OnosSystemTest/TestON/tests/FUNC/FUNCnetCfg/dependencies/network-cfg.json"
+        dstPath = "~/onos/tools/package/config/network-cfg.json"
+        prestartResult = main.ONOSbench.scp( main.ONOSbench, srcPath, dstPath, direction="to" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=prestartResult,
+                                 onpass="Successfully copied network-cfg.json to target directory",
+                                 onfail="Failed to copy network-cfg.json to target directory" )
+
+    def CASE26( self, main ):
+        """
+            Check to see that pre-startup configurations were set correctly
+        """
+        import json
+        main.case( "Check to see if the pre-startup configurations were set, then remove their allowed status" )
+        main.step( "Checking configurations for Switches 5 and 6" )
+        main.step( "ONOS should only show devices S1, S2, S4, S5, and S6" )
+        devices = main.ONOSrest1.devices()
+        main.log.debug( main.ONOSrest1.pprint( devices ) )
+        allowedDevices = [ "of:{}".format( str( i ).zfill( 16 ) ) for i in [ 1, 2, 4, 5, 6 ] ]
+        main.log.debug( allowedDevices )
+        onosDevices = []
+        try:
+            for sw in json.loads( devices ):
+                onosDevices.append( str( sw['id'] ) )
+            onosDevices.sort()
+            main.log.debug( onosDevices )
+        except( TypeError, ValueError ):
+            main.log.error( "Problem loading devices" )
+        utilities.assert_equals( expect=allowedDevices,
+                                 actual=onosDevices,
+                                 onpass="Only allowed devices are in ONOS",
+                                 onfail="ONOS devices doesn't match the list" +
+                                        " of allowed devices" )
+
+        main.step( "Removing allowed status from Switches 5 and 6" )
+        main.s5Json = { "allowed": False }
+        main.s6Json = { "allowed": False }
+        s5Json = main.s5Json
+        setS1 = main.ONOSrest1.setNetCfg( s5Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000005",
+                                          configKey="basic" )
+
+        s6Json = main.s6Json
+        setS1 = main.ONOSrest1.setNetCfg( s6Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000006",
+                                          configKey="basic" )
\ No newline at end of file
diff --git a/TestON/tests/FUNC/FUNCnetCfg/dependencies/network-cfg.json b/TestON/tests/FUNC/FUNCnetCfg/dependencies/network-cfg.json
new file mode 100644
index 0000000..92f647a
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCnetCfg/dependencies/network-cfg.json
@@ -0,0 +1,10 @@
+{
+    "devices": {
+        "of:0000000000000005": {
+            "allowed": true
+            },
+        "of:0000000000000006": {
+            "allowed": true
+            }
+        }
+    }
\ No newline at end of file
diff --git a/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.params b/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.params
index fc296cc..f5339f2 100644
--- a/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.params
+++ b/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.params
@@ -2,12 +2,13 @@
     # CASE - Description
     # 1 - Variable initialization and optional pull and build ONOS package
     # 2 - Install ONOS
+    # 19 - Copy karaf logs from ONOS nodes to TestON log directory
     # 100 - Ensure netconf app is running
     # 200 - Create or modify a configuration file
     # 300 - Push a configuration file to bring up a device
     # 400 - Bring down a device (not yet possible)
 
-    <testcases>1,[2,100,200,300]*2</testcases>
+    <testcases>1,[2,100,200,300,19]*2</testcases>
 
     <SCALE>
         <size>1,3</size>
diff --git a/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.py b/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.py
index ddd1cd8..518ce68 100644
--- a/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.py
+++ b/TestON/tests/FUNC/FUNCnetconf/FUNCnetconf.py
@@ -55,6 +55,7 @@
             main.configName = main.params[ 'CONFIGURE' ][ 'cfgName' ]
             main.configPass = main.params[ 'CONFIGURE' ][ 'cfgPass' ]
             main.configPort = main.params[ 'CONFIGURE' ][ 'cfgAppPort' ]
+            main.cycle = 0 # How many times FUNCintent has run through its tests
 
             gitPull = main.params[ 'GIT' ][ 'pull' ]
             main.cellData = {} # for creating cell file
@@ -139,6 +140,8 @@
         - Connect to cli
         """
 
+        main.cycle += 1
+
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.numCtrls = int( main.scale[ 0 ] )
 
@@ -254,6 +257,39 @@
         # Remove the first element in main.scale list
         main.scale.remove( main.scale[ 0 ] )
 
+    def CASE19( self, main ):
+        """
+            Copy the karaf.log files after each testcase cycle
+        """
+        main.log.report( "Copy karaf logs" )
+        main.case( "Copy karaf logs" )
+        main.caseExplanation = "Copying the karaf logs to preserve them through" +\
+                               "reinstalling ONOS"
+        main.step( "Copying karaf logs" )
+        stepResult = main.TRUE
+        scpResult = main.TRUE
+        copyResult = main.TRUE
+        i = 0
+        for cli in main.CLIs2:
+            main.node = cli
+            ip = main.ONOSip[ i ]
+            main.node.ip_address = ip
+            scpResult = scpResult and main.ONOSbench.scp( main.node ,
+                                            "/opt/onos/log/karaf.log",
+                                            "/tmp/karaf.log",
+                                            direction="from" )
+            copyResult = copyResult and main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,
+                                                                    copyFileName=( "karaf.log.node{0}.cycle{1}".format( str( i + 1 ), str( main.cycle ) ) ) )
+            if scpResult and copyResult:
+                stepResult =  main.TRUE and stepResult
+            else:
+                stepResult = main.FALSE and stepResult
+            i += 1
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully copied remote ONOS logs",
+                                 onfail="Failed to copy remote ONOS logs" )
+
     def CASE100( self, main ):
         """
             Start NETCONF app and OFC-Server or make sure that they are already running
diff --git a/TestON/tests/FUNC/FUNCnetconf/dependencies/netconf.py b/TestON/tests/FUNC/FUNCnetconf/dependencies/netconf.py
index 631bd84..a97b2eb 100644
--- a/TestON/tests/FUNC/FUNCnetconf/dependencies/netconf.py
+++ b/TestON/tests/FUNC/FUNCnetconf/dependencies/netconf.py
@@ -89,13 +89,11 @@
         This function prepares the command needed to upload the configuration
         file to the REST API
     """
-    ip = main.ONOSip[0]
-    port = 8181
     url = "/network/configuration"
     method = "POST"
     data = main.cfgJson
     configResult = main.FALSE
-    sendResult = main.CLIs[ 0 ].send( ip=ip, port=port, url=url, method=method, data=data )
+    sendResult = main.CLIs[ 0 ].send( url=url, method=method, data=data )
     main.log.info( "Device configuration request response code: " + str( sendResult[ 0 ] ) )
     if ( 200 <= sendResult[ 0 ] <= 299):
         configResult = main.TRUE
diff --git a/TestON/tests/FUNC/FUNCoptical/FUNCoptical.params b/TestON/tests/FUNC/FUNCoptical/FUNCoptical.params
index a906f12..50bb652 100644
--- a/TestON/tests/FUNC/FUNCoptical/FUNCoptical.params
+++ b/TestON/tests/FUNC/FUNCoptical/FUNCoptical.params
@@ -5,13 +5,14 @@
     # 10 - Start Mininet opticalTest Topology
     # 14 - Stop Mininet
     # 17 - Activate Flow Objectives
+    # 19 - Copy karaf logs from ONOS nodes to TestON log directory
     # 21 - Run pingall to discover all hosts
     # 22 - Send arpings to discover all hosts
     # 23 - Compare ONOS Topology to Mininet Topology
     # 31 - Add and test bidirectional point intents
     # 32 - Add and test bidirectional host intents
 
-    <testcases>1,[2,10,21,22,23,31,32,14,2,10,21,22,23,31,32,14]*1,[2,10,17,21,22,23,31,32,14,2,10,17,21,22,23,31,32,14]*1</testcases>
+    <testcases>1,[2,10,21,22,23,31,32,14,19,2,10,21,22,23,31,32,14,19]*1,[2,10,17,21,22,23,31,32,14,19,2,10,17,21,22,23,31,32,14,19]*1</testcases>
 
     <SCALE>
         <size>1,3,1,3</size>
diff --git a/TestON/tests/FUNC/FUNCoptical/FUNCoptical.py b/TestON/tests/FUNC/FUNCoptical/FUNCoptical.py
index ab4d3d0..65c731b 100644
--- a/TestON/tests/FUNC/FUNCoptical/FUNCoptical.py
+++ b/TestON/tests/FUNC/FUNCoptical/FUNCoptical.py
@@ -57,6 +57,7 @@
             main.ONOSip = []  # List of IPs of active ONOS nodes. CASE 2
             main.activeONOSip = []
             main.assertReturnString = ''  # Assembled assert return string
+            main.cycle = 0 # How many times FUNCintent has run through its tests
 
             main.ONOSip = main.ONOSbench.getOnosIps()
 
@@ -124,6 +125,8 @@
         - Connect to cli
         """
 
+        main.cycle += 1
+
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.numCtrls = int( main.scale[ 0 ] )
         main.flowCompiler = "Flow Rules"
@@ -203,24 +206,22 @@
         onosIsUp = main.TRUE
 
         for i in range( main.numCtrls ):
-            onosIsUp = onosIsUp and main.ONOSbench.isup( main.ONOSip[ i ] )
-        if onosIsUp == main.TRUE:
-            main.log.report( "ONOS instance is up and ready" )
-        else:
-            main.log.report( "ONOS instance may not be up, stop and " +
-                             "start ONOS again " )
-
-            for i in range( main.numCtrls ):
-                stopResult = stopResult and \
-                        main.ONOSbench.onosStop( main.ONOSip[ i ] )
-            for i in range( main.numCtrls ):
-                startResult = startResult and \
-                        main.ONOSbench.onosStart( main.ONOSip[ i ] )
+            isUp = main.ONOSbench.isup( main.ONOSip[ i ] )
+            onosIsUp = onosIsUp and isUp
+            if isUp == main.TRUE:
+                main.log.report( "ONOS instance {0} is up and ready".format( i + 1 ) )
+            else:
+                main.log.report( "ONOS instance {0} may not be up, stop and ".format( i + 1 ) +
+                                 "start ONOS again " )
+                stopResult = stopResult and main.ONOSbench.onosStop( main.ONOSip[ i ] )
+                startResult = startResult and main.ONOSbench.onosStart( main.ONOSip[ i ] )
+                if not startResult or stopResult:
+                    main.log.report( "ONOS instance {0} did not start correctly.".format( i + 1) )
         stepResult = onosIsUp and stopResult and startResult
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="ONOS service is ready",
-                                 onfail="ONOS service did not start properly" )
+                                 onpass="ONOS service is ready on all nodes",
+                                 onfail="ONOS service did not start properly on all nodes" )
 
         main.step( "Start ONOS cli" )
         cliResult = main.TRUE
@@ -304,6 +305,39 @@
                                  onpass="Successfully activated Flow Objectives",
                                  onfail="Failed to activate Flow Objectives" )
 
+    def CASE19( self, main ):
+        """
+            Copy the karaf.log files after each testcase cycle
+        """
+        main.log.report( "Copy karaf logs" )
+        main.case( "Copy karaf logs" )
+        main.caseExplanation = "Copying the karaf logs to preserve them through" +\
+                               "reinstalling ONOS"
+        main.step( "Copying karaf logs" )
+        stepResult = main.TRUE
+        scpResult = main.TRUE
+        copyResult = main.TRUE
+        i = 0
+        for cli in main.CLIs:
+            main.node = cli
+            ip = main.ONOSip[ i ]
+            main.node.ip_address = ip
+            scpResult = scpResult and main.ONOSbench.scp( main.node ,
+                                            "/opt/onos/log/karaf.log",
+                                            "/tmp/karaf.log",
+                                            direction="from" )
+            copyResult = copyResult and main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,
+                                                                    copyFileName=( "karaf.log.node{0}.cycle{1}".format( str( i + 1 ), str( main.cycle ) ) ) )
+            if scpResult and copyResult:
+                stepResult =  main.TRUE and stepResult
+            else:
+                stepResult = main.FALSE and stepResult
+            i += 1
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully copied remote ONOS logs",
+                                 onfail="Failed to copy remote ONOS logs" )
+
     def CASE21( self,main ):
         """
             Run pingall to discover all hosts
@@ -311,7 +345,7 @@
         main.case( "Running Pingall" )
         main.caseExplanation = "Use pingall to discover all hosts. Pingall is expected to fail."
         main.step( "Discover Hosts through Pingall" )
-        pingResult = main.LincOE.pingall( timeout = 600 )
+        pingResult = main.LincOE.pingall( timeout = 120 )
 
         utilities.assert_equals( expect=main.FALSE,
                                  actual=pingResult,
diff --git a/TestON/tests/FUNC/FUNCoptical/dependencies/FuncIntentFunction.py b/TestON/tests/FUNC/FUNCoptical/dependencies/FuncIntentFunction.py
index 377fd20..1d45f62 100644
--- a/TestON/tests/FUNC/FUNCoptical/dependencies/FuncIntentFunction.py
+++ b/TestON/tests/FUNC/FUNCoptical/dependencies/FuncIntentFunction.py
@@ -1437,7 +1437,7 @@
 
     for i in range( main.numCtrls ):
         topologyResult = main.CLIs[ i ].topology()
-        statusResult = main.ONOSbench.checkStatus( topologyResult,
+        statusResult = main.CLIs[ i ].checkStatus( topologyResult,
                                                    main.numSwitch,
                                                    expectedLink )\
                        and statusResult
diff --git a/TestON/tests/HA/HAclusterRestart/HAclusterRestart.params b/TestON/tests/HA/HAclusterRestart/HAclusterRestart.params
index 28b1e9b..a77e100 100644
--- a/TestON/tests/HA/HAclusterRestart/HAclusterRestart.params
+++ b/TestON/tests/HA/HAclusterRestart/HAclusterRestart.params
@@ -17,7 +17,7 @@
     #CASE15: Check that Leadership Election is still functional
     #CASE16: Install Distributed Primitives app
     #CASE17: Check for basic functionality with distributed primitives
-    <testcases>1,2,8,3,8,4,5,14,16,17,[6],8,3,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+    <testcases>1,2,8,21,3,8,4,5,14,16,17,[6],8,3,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
 
     <apps></apps>
     <ONOS_Configuration>
diff --git a/TestON/tests/HA/HAclusterRestart/HAclusterRestart.py b/TestON/tests/HA/HAclusterRestart/HAclusterRestart.py
index c229b03..69f094d 100644
--- a/TestON/tests/HA/HAclusterRestart/HAclusterRestart.py
+++ b/TestON/tests/HA/HAclusterRestart/HAclusterRestart.py
@@ -261,12 +261,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -1680,8 +1679,9 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
-        if consistentClustersResult != main.TRUE:
+        if not consistentClustersResult:
             main.log.debug( clusters )
+
         # there should always only be one cluster
         main.step( "Cluster view correct across ONOS nodes" )
         try:
@@ -1853,7 +1853,8 @@
 
         for i in range( 10 ):
             ready = True
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 output = cli.summary()
                 if not output:
                     ready = False
@@ -1874,7 +1875,8 @@
 
         # Rerun for election on restarted nodes
         runResults = main.TRUE
-        for cli in main.CLIs:
+        for i in main.activeNodes:
+            cli = main.CLIs[i]
             run = cli.electionTestRun()
             if run != main.TRUE:
                 main.log.error( "Error running for election on " + cli.name )
@@ -2554,6 +2556,8 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
 
         main.step( "There is only one SCC" )
         # there should always only be one cluster
@@ -4211,6 +4215,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.params b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.params
index 478bff1..eb758d2 100644
--- a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.params
+++ b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.params
@@ -19,7 +19,7 @@
     #CASE15: Check that Leadership Election is still functional
     #CASE16: Install Distributed Primitives app
     #CASE17: Check for basic functionality with distributed primitives
-    <testcases>1,[2,8,3,4,5,14,16,17]*1,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+    <testcases>1,[2,8,21,3,4,5,14,16,17]*1,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
 
     <apps></apps>
     <ONOS_Configuration>
diff --git a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
index dd1e1fb..a48a460 100644
--- a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
+++ b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
@@ -286,12 +286,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -1682,8 +1681,9 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
-        if consistentClustersResult != main.TRUE:
+        if not consistentClustersResult:
             main.log.debug( clusters )
+
         # there should always only be one cluster
         main.step( "Cluster view correct across ONOS nodes" )
         try:
@@ -2540,6 +2540,8 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
 
         main.step( "There is only one SCC" )
         # there should always only be one cluster
@@ -4199,6 +4201,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAkillNodes/HAkillNodes.params b/TestON/tests/HA/HAkillNodes/HAkillNodes.params
index d2eb635..dd035f5 100644
--- a/TestON/tests/HA/HAkillNodes/HAkillNodes.params
+++ b/TestON/tests/HA/HAkillNodes/HAkillNodes.params
@@ -19,7 +19,7 @@
     #CASE15: Check that Leadership Election is still functional
     #CASE16: Install Distributed Primitives app
     #CASE17: Check for basic functionality with distributed primitives
-    <testcases>1,2,8,3,4,5,14,16,17,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+    <testcases>1,2,8,21,3,4,5,14,16,17,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
 
     <apps></apps>
     <ONOS_Configuration>
diff --git a/TestON/tests/HA/HAkillNodes/HAkillNodes.py b/TestON/tests/HA/HAkillNodes/HAkillNodes.py
index 52a242c..6fb4d6c 100644
--- a/TestON/tests/HA/HAkillNodes/HAkillNodes.py
+++ b/TestON/tests/HA/HAkillNodes/HAkillNodes.py
@@ -297,12 +297,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -1702,8 +1701,9 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
-        if consistentClustersResult != main.TRUE:
+        if not consistentClustersResult:
             main.log.debug( clusters )
+
         # there should always only be one cluster
         main.step( "Cluster view correct across ONOS nodes" )
         try:
@@ -2575,6 +2575,8 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
 
         main.step( "There is only one SCC" )
         # there should always only be one cluster
@@ -4234,6 +4236,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAsanity/HAsanity.py b/TestON/tests/HA/HAsanity/HAsanity.py
index 3c77dd0..aa66574 100644
--- a/TestON/tests/HA/HAsanity/HAsanity.py
+++ b/TestON/tests/HA/HAsanity/HAsanity.py
@@ -262,12 +262,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -1668,8 +1667,9 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
-        if consistentClustersResult != main.TRUE:
+        if not consistentClustersResult:
             main.log.debug( clusters )
+
         # there should always only be one cluster
         main.step( "Cluster view correct across ONOS nodes" )
         try:
@@ -2476,6 +2476,8 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
 
         main.step( "There is only one SCC" )
         # there should always only be one cluster
@@ -4135,6 +4137,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAscaling/HAscaling.params b/TestON/tests/HA/HAscaling/HAscaling.params
new file mode 100644
index 0000000..388f432
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/HAscaling.params
@@ -0,0 +1,93 @@
+<PARAMS>
+    #CASE1: Compile ONOS and push it to the test machines
+    #CASE2: Assign devices to controllers
+    #CASE21: Assign mastership to controllers
+    #CASE3: Assign intents
+    #CASE4: Ping across added host intents
+    #CASE5: Reading state of ONOS
+    #CASE6: The Failure case.
+    #CASE7: Check state after control plane failure
+    #CASE8: Compare topo
+    #CASE9: Link s3-s28 down
+    #CASE10: Link s3-s28 up
+    #CASE11: Switch down
+    #CASE12: Switch up
+    #CASE13: Clean up
+    #CASE14: start election app on all onos nodes
+    #CASE15: Check that Leadership Election is still functional
+    #CASE16: Install Distributed Primitives app
+    #CASE17: Check for basic functionality with distributed primitives
+    <testcases>1,2,8,21,3,8,4,5,14,16,17,[6,8,7,4,15,17]*9,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+
+    <scaling>1,3b,5b,7b,7,7b,5b,3b,1</scaling>
+    <server>
+        <port>8000</port>
+        <interface>eth0</interface>
+    </server>
+    <apps></apps>
+    <ONOS_Configuration>
+        <org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+            <useFlowObjectives>true</useFlowObjectives>
+        </org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+    </ONOS_Configuration>
+    <ENV>
+        <cellName>HA</cellName>
+        <appString>drivers,openflow,proxyarp,mobility</appString>
+    </ENV>
+    <Git> False </Git>
+    <branch> master </branch>
+    <num_controllers> 7 </num_controllers>
+    <tcpdump> False </tcpdump>
+
+    <CTRL>
+        <port1>6653</port1>
+        <port2>6653</port2>
+        <port3>6653</port3>
+        <port4>6653</port4>
+        <port5>6653</port5>
+        <port6>6653</port6>
+        <port7>6653</port7>
+    </CTRL>
+    <BACKUP>
+        <ENABLED> False </ENABLED>
+        <TESTONUSER>sdn</TESTONUSER>
+        <TESTONIP>10.128.30.9</TESTONIP>
+    </BACKUP>
+    <PING>
+        <source1>h8</source1>
+        <source2>h9</source2>
+        <source3>h10</source3>
+        <source4>h11</source4>
+        <source5>h12</source5>
+        <source6>h13</source6>
+        <source7>h14</source7>
+        <source8>h15</source8>
+        <source9>h16</source9>
+        <source10>h17</source10>
+        <target1>10.0.0.18</target1>
+        <target2>10.0.0.19</target2>
+        <target3>10.0.0.20</target3>
+        <target4>10.0.0.21</target4>
+        <target5>10.0.0.22</target5>
+        <target6>10.0.0.23</target6>
+        <target7>10.0.0.24</target7>
+        <target8>10.0.0.25</target8>
+        <target9>10.0.0.26</target9>
+        <target10>10.0.0.27</target10>
+    </PING>
+    <timers>
+        <LinkDiscovery>12</LinkDiscovery>
+        <SwitchDiscovery>12</SwitchDiscovery>
+        <gossip>5</gossip>
+    </timers>
+    <kill>
+        <switch> s5 </switch>
+        <dpid> 0000000000005000 </dpid>
+        <links> h5 s2 s1 s6 </links>
+    </kill>
+    <MNtcpdump>
+        <intf>eth0</intf>
+        <port> </port>
+        <folder>~/packet_captures/</folder>
+    </MNtcpdump>
+</PARAMS>
diff --git a/TestON/tests/HA/HAscaling/HAscaling.py b/TestON/tests/HA/HAscaling/HAscaling.py
new file mode 100644
index 0000000..2064f1e
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/HAscaling.py
@@ -0,0 +1,4296 @@
+"""
+Description: This test is to determine if ONOS can handle
+             dynamic scaling of the cluster size.
+
+List of test cases:
+CASE1: Compile ONOS and push it to the test machines
+CASE2: Assign devices to controllers
+CASE21: Assign mastership to controllers
+CASE3: Assign intents
+CASE4: Ping across added host intents
+CASE5: Reading state of ONOS
+CASE6: The scaling case.
+CASE7: Check state after control plane failure
+CASE8: Compare topo
+CASE9: Link s3-s28 down
+CASE10: Link s3-s28 up
+CASE11: Switch down
+CASE12: Switch up
+CASE13: Clean up
+CASE14: start election app on all onos nodes
+CASE15: Check that Leadership Election is still functional
+CASE16: Install Distributed Primitives app
+CASE17: Check for basic functionality with distributed primitives
+"""
+
+
+class HAscaling:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE1( self, main ):
+        """
+        CASE1 is to compile ONOS and push it to the test machines
+
+        Startup sequence:
+        cell <name>
+        onos-verify-cell
+        NOTE: temporary - onos-remove-raft-logs
+        onos-uninstall
+        start mininet
+        git pull
+        mvn clean install
+        onos-package
+        onos-install -f
+        onos-wait-for-start
+        start cli sessions
+        start tcpdump
+        """
+        import time
+        import os
+        import re
+        main.log.info( "ONOS HA test: Restart all ONOS nodes - " +
+                         "initialization" )
+        main.case( "Setting up test environment" )
+        main.caseExplanation = "Setup the test environment including " +\
+                                "installing ONOS, starting Mininet and ONOS" +\
+                                "cli sessions."
+
+        # load some variables from the params file
+        PULLCODE = False
+        if main.params[ 'Git' ] == 'True':
+            PULLCODE = True
+        gitBranch = main.params[ 'branch' ]
+        cellName = main.params[ 'ENV' ][ 'cellName' ]
+
+        main.numCtrls = int( main.params[ 'num_controllers' ] )
+        if main.ONOSbench.maxNodes:
+            if main.ONOSbench.maxNodes < main.numCtrls:
+                main.numCtrls = int( main.ONOSbench.maxNodes )
+        # set global variables
+        # These are for csv plotting in jenkins
+        global labels
+        global data
+        labels = []
+        data = []
+
+        try:
+            from tests.HA.dependencies.HA import HA
+            main.HA = HA()
+            from tests.HA.HAscaling.dependencies.Server import Server
+            main.Server = Server()
+        except Exception as e:
+            main.log.exception( e )
+            main.cleanup()
+            main.exit()
+
+        main.CLIs = []
+        main.nodes = []
+        ipList = []
+        for i in range( 1, main.numCtrls + 1 ):
+            try:
+                main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
+                main.nodes.append( getattr( main, 'ONOS' + str( i ) ) )
+                ipList.append( main.nodes[ -1 ].ip_address )
+            except AttributeError:
+                break
+
+        main.step( "Create cell file" )
+        cellAppString = main.params[ 'ENV' ][ 'appString' ]
+        main.ONOSbench.createCellFile( main.ONOSbench.ip_address, cellName,
+                                       main.Mininet1.ip_address,
+                                       cellAppString, ipList )
+
+        main.step( "Applying cell variable to environment" )
+        cellResult = main.ONOSbench.setCell( cellName )
+        utilities.assert_equals( expect=main.TRUE, actual=cellResult,
+                                 onpass="Set cell successfull",
+                                 onfail="Failled to set cell" )
+
+        main.step( "Verify connectivity to cell" )
+        verifyResult = main.ONOSbench.verifyCell()
+        utilities.assert_equals( expect=main.TRUE, actual=verifyResult,
+                                 onpass="Verify cell passed",
+                                 onfail="Failled to verify cell" )
+
+        # FIXME:this is short term fix
+        main.log.info( "Removing raft logs" )
+        main.ONOSbench.onosRemoveRaftLogs()
+
+        main.log.info( "Uninstalling ONOS" )
+        for node in main.nodes:
+            main.ONOSbench.onosUninstall( node.ip_address )
+
+        # Make sure ONOS is DEAD
+        main.log.info( "Killing any ONOS processes" )
+        killResults = main.TRUE
+        for node in main.nodes:
+            killed = main.ONOSbench.onosKill( node.ip_address )
+            killResults = killResults and killed
+
+        main.step( "Setup server for cluster metadata file" )
+        port = main.params['server']['port']
+        rootDir = os.path.dirname( main.testFile ) + "/dependencies"
+        main.log.debug( "Root dir: {}".format( rootDir ) )
+        status = main.Server.start( main.ONOSbench,
+                                    rootDir,
+                                    port=port,
+                                    logDir=main.logdir + "/server.log" )
+        utilities.assert_equals( expect=main.TRUE, actual=status,
+                                 onpass="Server started",
+                                 onfail="Failled to start SimpleHTTPServer" )
+
+        main.step( "Generate initial metadata file" )
+        main.scaling = main.params['scaling'].split( "," )
+        main.log.debug( main.scaling )
+        scale = main.scaling.pop(0)
+        main.log.debug( scale)
+        if "e" in scale:
+            equal = True
+        else:
+            equal = False
+        main.log.debug( equal)
+        main.numCtrls = int( re.search( "\d+", scale ).group(0) )
+        genResult = main.Server.generateFile( main.numCtrls, equal=equal )
+        utilities.assert_equals( expect=main.TRUE, actual=genResult,
+                                 onpass="New cluster metadata file generated",
+                                 onfail="Failled to generate new metadata file" )
+
+        cleanInstallResult = main.TRUE
+        gitPullResult = main.TRUE
+
+        main.step( "Starting Mininet" )
+        # scp topo file to mininet
+        # TODO: move to params?
+        topoName = "obelisk.py"
+        filePath = main.ONOSbench.home + "/tools/test/topos/"
+        main.ONOSbench.scp( main.Mininet1,
+                            filePath + topoName,
+                            main.Mininet1.home,
+                            direction="to" )
+        mnResult = main.Mininet1.startNet( )
+        utilities.assert_equals( expect=main.TRUE, actual=mnResult,
+                                 onpass="Mininet Started",
+                                 onfail="Error starting Mininet" )
+
+        main.step( "Git checkout and pull " + gitBranch )
+        if PULLCODE:
+            main.ONOSbench.gitCheckout( gitBranch )
+            gitPullResult = main.ONOSbench.gitPull()
+            # values of 1 or 3 are good
+            utilities.assert_lesser( expect=0, actual=gitPullResult,
+                                      onpass="Git pull successful",
+                                      onfail="Git pull failed" )
+        main.ONOSbench.getVersion( report=True )
+
+        main.step( "Using mvn clean install" )
+        cleanInstallResult = main.TRUE
+        if PULLCODE and gitPullResult == main.TRUE:
+            cleanInstallResult = main.ONOSbench.cleanInstall()
+        else:
+            main.log.warn( "Did not pull new code so skipping mvn " +
+                           "clean install" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cleanInstallResult,
+                                 onpass="MCI successful",
+                                 onfail="MCI failed" )
+        # GRAPHS
+        # NOTE: important params here:
+        #       job = name of Jenkins job
+        #       Plot Name = Plot-HA, only can be used if multiple plots
+        #       index = The number of the graph under plot name
+        job = "HAscaling"
+        plotName = "Plot-HA"
+        index = "0"
+        graphs = '<ac:structured-macro ac:name="html">\n'
+        graphs += '<ac:plain-text-body><![CDATA[\n'
+        graphs += '<iframe src="https://onos-jenkins.onlab.us/job/' + job +\
+                  '/plot/' + plotName + '/getPlot?index=' + index +\
+                  '&width=500&height=300"' +\
+                  'noborder="0" width="500" height="300" scrolling="yes" ' +\
+                  'seamless="seamless"></iframe>\n'
+        graphs += ']]></ac:plain-text-body>\n'
+        graphs += '</ac:structured-macro>\n'
+        main.log.wiki(graphs)
+
+        main.step( "Copying backup config files" )
+        path = "~/onos/tools/package/bin/onos-service"
+        cp = main.ONOSbench.scp( main.ONOSbench,
+                                     path,
+                                     path + ".backup",
+                                     direction="to" )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cp,
+                                 onpass="Copy backup config file succeeded",
+                                 onfail="Copy backup config file failed" )
+        # we need to modify the onos-service file to use remote metadata file
+        # url for cluster metadata file
+        iface = main.params['server'].get( 'interface' )
+        ip = main.ONOSbench.getIpAddr( iface=iface )
+        metaFile = "cluster.json"
+        javaArgs = r"-Donos.cluster.metadata.uri=http:\/\/{}:{}\/{}".format( ip, port, metaFile )
+        main.log.warn( javaArgs )
+        main.log.warn( repr( javaArgs ) )
+        handle = main.ONOSbench.handle
+        sed = r"sed -i 's/bash/bash\nexport JAVA_OPTS=${{JAVA_OPTS:-{}}}\n/' {}".format( javaArgs, path )
+        main.log.warn( sed )
+        main.log.warn( repr( sed ) )
+        handle.sendline( sed )
+        handle.expect( "\$" )
+        main.log.debug( repr( handle.before ) )
+
+        main.step( "Creating ONOS package" )
+        packageResult = main.ONOSbench.onosPackage()
+        utilities.assert_equals( expect=main.TRUE, actual=packageResult,
+                                 onpass="ONOS package successful",
+                                 onfail="ONOS package failed" )
+
+        main.step( "Installing ONOS package" )
+        onosInstallResult = main.TRUE
+        for i in range( main.ONOSbench.maxNodes ):
+            node = main.nodes[i]
+            options = "-f"
+            if i >= main.numCtrls:
+                options = "-nf"  # Don't start more than the current scale
+            tmpResult = main.ONOSbench.onosInstall( options=options,
+                                                    node=node.ip_address )
+            onosInstallResult = onosInstallResult and tmpResult
+        utilities.assert_equals( expect=main.TRUE, actual=onosInstallResult,
+                                 onpass="ONOS install successful",
+                                 onfail="ONOS install failed" )
+
+        # Cleanup custom onos-service file
+        main.ONOSbench.scp( main.ONOSbench,
+                            path + ".backup",
+                            path,
+                            direction="to" )
+
+        main.step( "Checking if ONOS is up yet" )
+        for i in range( 2 ):
+            onosIsupResult = main.TRUE
+            for i in range( main.numCtrls ):
+                node = main.nodes[i]
+                started = main.ONOSbench.isup( node.ip_address )
+                if not started:
+                    main.log.error( node.name + " hasn't started" )
+                onosIsupResult = onosIsupResult and started
+            if onosIsupResult == main.TRUE:
+                break
+        utilities.assert_equals( expect=main.TRUE, actual=onosIsupResult,
+                                 onpass="ONOS startup successful",
+                                 onfail="ONOS startup failed" )
+
+        main.log.step( "Starting ONOS CLI sessions" )
+        cliResults = main.TRUE
+        threads = []
+        for i in range( main.numCtrls ):
+            t = main.Thread( target=main.CLIs[i].startOnosCli,
+                             name="startOnosCli-" + str( i ),
+                             args=[main.nodes[i].ip_address] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            cliResults = cliResults and t.result
+        utilities.assert_equals( expect=main.TRUE, actual=cliResults,
+                                 onpass="ONOS cli startup successful",
+                                 onfail="ONOS cli startup failed" )
+
+        # Create a list of active nodes for use when some nodes are stopped
+        main.activeNodes = [ i for i in range( 0, main.numCtrls ) ]
+
+        if main.params[ 'tcpdump' ].lower() == "true":
+            main.step( "Start Packet Capture MN" )
+            main.Mininet2.startTcpdump(
+                str( main.params[ 'MNtcpdump' ][ 'folder' ] ) + str( main.TEST )
+                + "-MN.pcap",
+                intf=main.params[ 'MNtcpdump' ][ 'intf' ],
+                port=main.params[ 'MNtcpdump' ][ 'port' ] )
+
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+
+        if not nodeResults:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
+                main.log.debug( "{} components not ACTIVE: \n{}".format(
+                    cli.name,
+                    cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
+            main.log.error( "Failed to start ONOS, stopping test" )
+            main.cleanup()
+            main.exit()
+
+        main.step( "Activate apps defined in the params file" )
+        # get data from the params
+        apps = main.params.get( 'apps' )
+        if apps:
+            apps = apps.split(',')
+            main.log.warn( apps )
+            activateResult = True
+            for app in apps:
+                main.CLIs[ 0 ].app( app, "Activate" )
+            # TODO: check this worked
+            time.sleep( 10 )  # wait for apps to activate
+            for app in apps:
+                state = main.CLIs[ 0 ].appStatus( app )
+                if state == "ACTIVE":
+                    activateResult = activateResult and True
+                else:
+                    main.log.error( "{} is in {} state".format( app, state ) )
+                    activateResult = False
+            utilities.assert_equals( expect=True,
+                                     actual=activateResult,
+                                     onpass="Successfully activated apps",
+                                     onfail="Failed to activate apps" )
+        else:
+            main.log.warn( "No apps were specified to be loaded after startup" )
+
+        main.step( "Set ONOS configurations" )
+        config = main.params.get( 'ONOS_Configuration' )
+        if config:
+            main.log.debug( config )
+            checkResult = main.TRUE
+            for component in config:
+                for setting in config[component]:
+                    value = config[component][setting]
+                    check = main.CLIs[ 0 ].setCfg( component, setting, value )
+                    main.log.info( "Value was changed? {}".format( main.TRUE == check ) )
+                    checkResult = check and checkResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=checkResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+
+        main.step( "App Ids check" )
+        appCheck = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck = appCheck and t.result
+        if appCheck != main.TRUE:
+            node = main.activeNodes[0]
+            main.log.warn( main.CLIs[node].apps() )
+            main.log.warn( main.CLIs[node].appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+    def CASE2( self, main ):
+        """
+        Assign devices to controllers
+        """
+        import re
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Assigning devices to controllers" )
+        main.caseExplanation = "Assign switches to ONOS using 'ovs-vsctl' " +\
+                                "and check that an ONOS node becomes the " +\
+                                "master of the device."
+        main.step( "Assign switches to controllers" )
+
+        ipList = []
+        for i in range( main.ONOSbench.maxNodes ):
+            ipList.append( main.nodes[ i ].ip_address )
+        swList = []
+        for i in range( 1, 29 ):
+            swList.append( "s" + str( i ) )
+        main.Mininet1.assignSwController( sw=swList, ip=ipList )
+
+        mastershipCheck = main.TRUE
+        for i in range( 1, 29 ):
+            response = main.Mininet1.getSwController( "s" + str( i ) )
+            try:
+                main.log.info( str( response ) )
+            except Exception:
+                main.log.info( repr( response ) )
+            for node in main.nodes:
+                if re.search( "tcp:" + node.ip_address, response ):
+                    mastershipCheck = mastershipCheck and main.TRUE
+                else:
+                    main.log.error( "Error, node " + node.ip_address + " is " +
+                                    "not in the list of controllers s" +
+                                    str( i ) + " is connecting to." )
+                    mastershipCheck = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=mastershipCheck,
+            onpass="Switch mastership assigned correctly",
+            onfail="Switches not assigned correctly to controllers" )
+
+    def CASE21( self, main ):
+        """
+        Assign mastership to controllers
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Assigning Controller roles for switches" )
+        main.caseExplanation = "Check that ONOS is connected to each " +\
+                                "device. Then manually assign" +\
+                                " mastership to specific ONOS nodes using" +\
+                                " 'device-role'"
+        main.step( "Assign mastership of switches to specific controllers" )
+        # Manually assign mastership to the controller we want
+        roleCall = main.TRUE
+
+        ipList = [ ]
+        deviceList = []
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        try:
+            # Assign mastership to specific controllers. This assignment was
+            # determined for a 7 node cluser, but will work with any sized
+            # cluster
+            for i in range( 1, 29 ):  # switches 1 through 28
+                # set up correct variables:
+                if i == 1:
+                    c = 0
+                    ip = main.nodes[ c ].ip_address  # ONOS1
+                    deviceId = onosCli.getDevice( "1000" ).get( 'id' )
+                elif i == 2:
+                    c = 1 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS2
+                    deviceId = onosCli.getDevice( "2000" ).get( 'id' )
+                elif i == 3:
+                    c = 1 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS2
+                    deviceId = onosCli.getDevice( "3000" ).get( 'id' )
+                elif i == 4:
+                    c = 3 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS4
+                    deviceId = onosCli.getDevice( "3004" ).get( 'id' )
+                elif i == 5:
+                    c = 2 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS3
+                    deviceId = onosCli.getDevice( "5000" ).get( 'id' )
+                elif i == 6:
+                    c = 2 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS3
+                    deviceId = onosCli.getDevice( "6000" ).get( 'id' )
+                elif i == 7:
+                    c = 5 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS6
+                    deviceId = onosCli.getDevice( "6007" ).get( 'id' )
+                elif i >= 8 and i <= 17:
+                    c = 4 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS5
+                    dpid = '3' + str( i ).zfill( 3 )
+                    deviceId = onosCli.getDevice( dpid ).get( 'id' )
+                elif i >= 18 and i <= 27:
+                    c = 6 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS7
+                    dpid = '6' + str( i ).zfill( 3 )
+                    deviceId = onosCli.getDevice( dpid ).get( 'id' )
+                elif i == 28:
+                    c = 0
+                    ip = main.nodes[ c ].ip_address  # ONOS1
+                    deviceId = onosCli.getDevice( "2800" ).get( 'id' )
+                else:
+                    main.log.error( "You didn't write an else statement for " +
+                                    "switch s" + str( i ) )
+                    roleCall = main.FALSE
+                # Assign switch
+                assert deviceId, "No device id for s" + str( i ) + " in ONOS"
+                # TODO: make this controller dynamic
+                roleCall = roleCall and onosCli.deviceRole( deviceId, ip )
+                ipList.append( ip )
+                deviceList.append( deviceId )
+        except ( AttributeError, AssertionError ):
+            main.log.exception( "Something is wrong with ONOS device view" )
+            main.log.info( onosCli.devices() )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=roleCall,
+            onpass="Re-assigned switch mastership to designated controller",
+            onfail="Something wrong with deviceRole calls" )
+
+        main.step( "Check mastership was correctly assigned" )
+        roleCheck = main.TRUE
+        # NOTE: This is due to the fact that device mastership change is not
+        #       atomic and is actually a multi step process
+        time.sleep( 5 )
+        for i in range( len( ipList ) ):
+            ip = ipList[i]
+            deviceId = deviceList[i]
+            # Check assignment
+            master = onosCli.getRole( deviceId ).get( 'master' )
+            if ip in master:
+                roleCheck = roleCheck and main.TRUE
+            else:
+                roleCheck = roleCheck and main.FALSE
+                main.log.error( "Error, controller " + ip + " is not" +
+                                " master " + "of device " +
+                                str( deviceId ) + ". Master is " +
+                                repr( master ) + "." )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=roleCheck,
+            onpass="Switches were successfully reassigned to designated " +
+                   "controller",
+            onfail="Switches were not successfully reassigned" )
+
+    def CASE3( self, main ):
+        """
+        Assign intents
+        """
+        import time
+        import json
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        try:
+            labels
+        except NameError:
+            main.log.error( "labels not defined, setting to []" )
+            labels = []
+        try:
+            data
+        except NameError:
+            main.log.error( "data not defined, setting to []" )
+            data = []
+        # NOTE: we must reinstall intents until we have a persistant intent
+        #        datastore!
+        main.case( "Adding host Intents" )
+        main.caseExplanation = "Discover hosts by using pingall then " +\
+                                "assign predetermined host-to-host intents." +\
+                                " After installation, check that the intent" +\
+                                " is distributed to all nodes and the state" +\
+                                " is INSTALLED"
+
+        # install onos-app-fwd
+        main.step( "Install reactive forwarding app" )
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        installResults = onosCli.activateApp( "org.onosproject.fwd" )
+        utilities.assert_equals( expect=main.TRUE, actual=installResults,
+                                 onpass="Install fwd successful",
+                                 onfail="Install fwd failed" )
+
+        main.step( "Check app ids" )
+        appCheck = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck = appCheck and t.result
+        if appCheck != main.TRUE:
+            main.log.warn( onosCli.apps() )
+            main.log.warn( onosCli.appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+        main.step( "Discovering Hosts( Via pingall for now )" )
+        # FIXME: Once we have a host discovery mechanism, use that instead
+        # REACTIVE FWD test
+        pingResult = main.FALSE
+        passMsg = "Reactive Pingall test passed"
+        time1 = time.time()
+        pingResult = main.Mininet1.pingall()
+        time2 = time.time()
+        if not pingResult:
+            main.log.warn("First pingall failed. Trying again...")
+            pingResult = main.Mininet1.pingall()
+            passMsg += " on the second try"
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=pingResult,
+            onpass= passMsg,
+            onfail="Reactive Pingall failed, " +
+                   "one or more ping pairs failed" )
+        main.log.info( "Time for pingall: %2f seconds" %
+                       ( time2 - time1 ) )
+        # timeout for fwd flows
+        time.sleep( 11 )
+        # uninstall onos-app-fwd
+        main.step( "Uninstall reactive forwarding app" )
+        node = main.activeNodes[0]
+        uninstallResult = main.CLIs[node].deactivateApp( "org.onosproject.fwd" )
+        utilities.assert_equals( expect=main.TRUE, actual=uninstallResult,
+                                 onpass="Uninstall fwd successful",
+                                 onfail="Uninstall fwd failed" )
+
+        main.step( "Check app ids" )
+        threads = []
+        appCheck2 = main.TRUE
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck2 = appCheck2 and t.result
+        if appCheck2 != main.TRUE:
+            node = main.activeNodes[0]
+            main.log.warn( main.CLIs[node].apps() )
+            main.log.warn( main.CLIs[node].appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck2,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+        main.step( "Add host intents via cli" )
+        intentIds = []
+        # TODO: move the host numbers to params
+        #       Maybe look at all the paths we ping?
+        intentAddResult = True
+        hostResult = main.TRUE
+        for i in range( 8, 18 ):
+            main.log.info( "Adding host intent between h" + str( i ) +
+                           " and h" + str( i + 10 ) )
+            host1 = "00:00:00:00:00:" + \
+                str( hex( i )[ 2: ] ).zfill( 2 ).upper()
+            host2 = "00:00:00:00:00:" + \
+                str( hex( i + 10 )[ 2: ] ).zfill( 2 ).upper()
+            # NOTE: getHost can return None
+            host1Dict = onosCli.getHost( host1 )
+            host2Dict = onosCli.getHost( host2 )
+            host1Id = None
+            host2Id = None
+            if host1Dict and host2Dict:
+                host1Id = host1Dict.get( 'id', None )
+                host2Id = host2Dict.get( 'id', None )
+            if host1Id and host2Id:
+                nodeNum = ( i % len( main.activeNodes ) )
+                node = main.activeNodes[nodeNum]
+                tmpId = main.CLIs[node].addHostIntent( host1Id, host2Id )
+                if tmpId:
+                    main.log.info( "Added intent with id: " + tmpId )
+                    intentIds.append( tmpId )
+                else:
+                    main.log.error( "addHostIntent returned: " +
+                                     repr( tmpId ) )
+            else:
+                main.log.error( "Error, getHost() failed for h" + str( i ) +
+                                " and/or h" + str( i + 10 ) )
+                node = main.activeNodes[0]
+                hosts = main.CLIs[node].hosts()
+                main.log.warn( "Hosts output: " )
+                try:
+                    main.log.warn( json.dumps( json.loads( hosts ),
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( repr( hosts ) )
+                hostResult = main.FALSE
+        utilities.assert_equals( expect=main.TRUE, actual=hostResult,
+                                 onpass="Found a host id for each host",
+                                 onfail="Error looking up host ids" )
+
+        intentStart = time.time()
+        onosIds = onosCli.getAllIntentsId()
+        main.log.info( "Submitted intents: " + str( intentIds ) )
+        main.log.info( "Intents in ONOS: " + str( onosIds ) )
+        for intent in intentIds:
+            if intent in onosIds:
+                pass  # intent submitted is in onos
+            else:
+                intentAddResult = False
+        if intentAddResult:
+            intentStop = time.time()
+        else:
+            intentStop = None
+        # Print the intent states
+        intents = onosCli.intents()
+        intentStates = []
+        installedCheck = True
+        main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+        count = 0
+        try:
+            for intent in json.loads( intents ):
+                state = intent.get( 'state', None )
+                if "INSTALLED" not in state:
+                    installedCheck = False
+                intentId = intent.get( 'id', None )
+                intentStates.append( ( intentId, state ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing intents" )
+        # add submitted intents not in the store
+        tmplist = [ i for i, s in intentStates ]
+        missingIntents = False
+        for i in intentIds:
+            if i not in tmplist:
+                intentStates.append( ( i, " - " ) )
+                missingIntents = True
+        intentStates.sort()
+        for i, s in intentStates:
+            count += 1
+            main.log.info( "%-6s%-15s%-15s" %
+                           ( str( count ), str( i ), str( s ) ) )
+        leaders = onosCli.leaders()
+        try:
+            missing = False
+            if leaders:
+                parsedLeaders = json.loads( leaders )
+                main.log.warn( json.dumps( parsedLeaders,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # check for all intent partitions
+                topics = []
+                for i in range( 14 ):
+                    topics.append( "intent-partition-" + str( i ) )
+                main.log.debug( topics )
+                ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                for topic in topics:
+                    if topic not in ONOStopics:
+                        main.log.error( "Error: " + topic +
+                                        " not in leaders" )
+                        missing = True
+            else:
+                main.log.error( "leaders() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing leaders" )
+            main.log.error( repr( leaders ) )
+        # Check all nodes
+        if missing:
+            for i in main.activeNodes:
+                response = main.CLIs[i].leaders( jsonFormat=False)
+                main.log.warn( str( main.CLIs[i].name ) + " leaders output: \n" +
+                               str( response ) )
+
+        partitions = onosCli.partitions()
+        try:
+            if partitions :
+                parsedPartitions = json.loads( partitions )
+                main.log.warn( json.dumps( parsedPartitions,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check for a leader in all paritions
+                # TODO check for consistency among nodes
+            else:
+                main.log.error( "partitions() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing partitions" )
+            main.log.error( repr( partitions ) )
+        pendingMap = onosCli.pendingMap()
+        try:
+            if pendingMap :
+                parsedPending = json.loads( pendingMap )
+                main.log.warn( json.dumps( parsedPending,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check something here?
+            else:
+                main.log.error( "pendingMap() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing pending map" )
+            main.log.error( repr( pendingMap ) )
+
+        intentAddResult = bool( intentAddResult and not missingIntents and
+                                installedCheck )
+        if not intentAddResult:
+            main.log.error( "Error in pushing host intents to ONOS" )
+
+        main.step( "Intent Anti-Entropy dispersion" )
+        for j in range(100):
+            correct = True
+            main.log.info( "Submitted intents: " + str( sorted( intentIds ) ) )
+            for i in main.activeNodes:
+                onosIds = []
+                ids = main.CLIs[i].getAllIntentsId()
+                onosIds.append( ids )
+                main.log.debug( "Intents in " + main.CLIs[i].name + ": " +
+                                str( sorted( onosIds ) ) )
+                if sorted( ids ) != sorted( intentIds ):
+                    main.log.warn( "Set of intent IDs doesn't match" )
+                    correct = False
+                    break
+                else:
+                    intents = json.loads( main.CLIs[i].intents() )
+                    for intent in intents:
+                        if intent[ 'state' ] != "INSTALLED":
+                            main.log.warn( "Intent " + intent[ 'id' ] +
+                                           " is " + intent[ 'state' ] )
+                            correct = False
+                            break
+            if correct:
+                break
+            else:
+                time.sleep(1)
+        if not intentStop:
+            intentStop = time.time()
+        global gossipTime
+        gossipTime = intentStop - intentStart
+        main.log.info( "It took about " + str( gossipTime ) +
+                        " seconds for all intents to appear in each node" )
+        append = False
+        title = "Gossip Intents"
+        count = 1
+        while append is False:
+            curTitle = title + str( count )
+            if curTitle not in labels:
+                labels.append( curTitle )
+                data.append( str( gossipTime ) )
+                append = True
+            else:
+                count += 1
+        gossipPeriod = int( main.params['timers']['gossip'] )
+        maxGossipTime = gossipPeriod * len( main.activeNodes )
+        utilities.assert_greater_equals(
+                expect=maxGossipTime, actual=gossipTime,
+                onpass="ECM anti-entropy for intents worked within " +
+                       "expected time",
+                onfail="Intent ECM anti-entropy took too long. " +
+                       "Expected time:{}, Actual time:{}".format( maxGossipTime,
+                                                                  gossipTime ) )
+        if gossipTime <= maxGossipTime:
+            intentAddResult = True
+
+        if not intentAddResult or "key" in pendingMap:
+            import time
+            installedCheck = True
+            main.log.info( "Sleeping 60 seconds to see if intents are found" )
+            time.sleep( 60 )
+            onosIds = onosCli.getAllIntentsId()
+            main.log.info( "Submitted intents: " + str( intentIds ) )
+            main.log.info( "Intents in ONOS: " + str( onosIds ) )
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            try:
+                for intent in json.loads( intents ):
+                    # Iter through intents of a node
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents" )
+            # add submitted intents not in the store
+            tmplist = [ i for i, s in intentStates ]
+            for i in intentIds:
+                if i not in tmplist:
+                    intentStates.append( ( i, " - " ) )
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            leaders = onosCli.leaders()
+            try:
+                missing = False
+                if leaders:
+                    parsedLeaders = json.loads( leaders )
+                    main.log.warn( json.dumps( parsedLeaders,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # check for all intent partitions
+                    # check for election
+                    topics = []
+                    for i in range( 14 ):
+                        topics.append( "intent-partition-" + str( i ) )
+                    # FIXME: this should only be after we start the app
+                    topics.append( "org.onosproject.election" )
+                    main.log.debug( topics )
+                    ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                    for topic in topics:
+                        if topic not in ONOStopics:
+                            main.log.error( "Error: " + topic +
+                                            " not in leaders" )
+                            missing = True
+                else:
+                    main.log.error( "leaders() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing leaders" )
+                main.log.error( repr( leaders ) )
+            # Check all nodes
+            if missing:
+                for i in main.activeNodes:
+                    node = main.CLIs[i]
+                    response = node.leaders( jsonFormat=False)
+                    main.log.warn( str( node.name ) + " leaders output: \n" +
+                                   str( response ) )
+
+            partitions = onosCli.partitions()
+            try:
+                if partitions :
+                    parsedPartitions = json.loads( partitions )
+                    main.log.warn( json.dumps( parsedPartitions,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check for a leader in all paritions
+                    # TODO check for consistency among nodes
+                else:
+                    main.log.error( "partitions() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing partitions" )
+                main.log.error( repr( partitions ) )
+            pendingMap = onosCli.pendingMap()
+            try:
+                if pendingMap :
+                    parsedPending = json.loads( pendingMap )
+                    main.log.warn( json.dumps( parsedPending,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check something here?
+                else:
+                    main.log.error( "pendingMap() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing pending map" )
+                main.log.error( repr( pendingMap ) )
+
+    def CASE4( self, main ):
+        """
+        Ping across added host intents
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        main.case( "Verify connectivity by sending traffic across Intents" )
+        main.caseExplanation = "Ping across added host intents to check " +\
+                                "functionality and check the state of " +\
+                                "the intent"
+
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        main.step( "Check Intent state" )
+        installedCheck = False
+        loopCount = 0
+        while not installedCheck and loopCount < 40:
+            installedCheck = True
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( intents ):
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents." )
+            # Print states
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            if not installedCheck:
+                time.sleep( 1 )
+                loopCount += 1
+        utilities.assert_equals( expect=True, actual=installedCheck,
+                                 onpass="Intents are all INSTALLED",
+                                 onfail="Intents are not all in " +
+                                        "INSTALLED state" )
+
+        main.step( "Ping across added host intents" )
+        PingResult = main.TRUE
+        for i in range( 8, 18 ):
+            ping = main.Mininet1.pingHost( src="h" + str( i ),
+                                           target="h" + str( i + 10 ) )
+            PingResult = PingResult and ping
+            if ping == main.FALSE:
+                main.log.warn( "Ping failed between h" + str( i ) +
+                               " and h" + str( i + 10 ) )
+            elif ping == main.TRUE:
+                main.log.info( "Ping test passed!" )
+                # Don't set PingResult or you'd override failures
+        if PingResult == main.FALSE:
+            main.log.error(
+                "Intents have not been installed correctly, pings failed." )
+            # TODO: pretty print
+            main.log.warn( "ONOS1 intents: " )
+            try:
+                tmpIntents = onosCli.intents()
+                main.log.warn( json.dumps( json.loads( tmpIntents ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+            except ( ValueError, TypeError ):
+                main.log.warn( repr( tmpIntents ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=PingResult,
+            onpass="Intents have been installed correctly and pings work",
+            onfail="Intents have not been installed correctly, pings failed." )
+
+        main.step( "Check leadership of topics" )
+        leaders = onosCli.leaders()
+        topicCheck = main.TRUE
+        try:
+            if leaders:
+                parsedLeaders = json.loads( leaders )
+                main.log.warn( json.dumps( parsedLeaders,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # check for all intent partitions
+                # check for election
+                # TODO: Look at Devices as topics now that it uses this system
+                topics = []
+                for i in range( 14 ):
+                    topics.append( "intent-partition-" + str( i ) )
+                # FIXME: this should only be after we start the app
+                # FIXME: topics.append( "org.onosproject.election" )
+                # Print leaders output
+                main.log.debug( topics )
+                ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                for topic in topics:
+                    if topic not in ONOStopics:
+                        main.log.error( "Error: " + topic +
+                                        " not in leaders" )
+                        topicCheck = main.FALSE
+            else:
+                main.log.error( "leaders() returned None" )
+                topicCheck = main.FALSE
+        except ( ValueError, TypeError ):
+            topicCheck = main.FALSE
+            main.log.exception( "Error parsing leaders" )
+            main.log.error( repr( leaders ) )
+            # TODO: Check for a leader of these topics
+        # Check all nodes
+        if topicCheck:
+            for i in main.activeNodes:
+                node = main.CLIs[i]
+                response = node.leaders( jsonFormat=False)
+                main.log.warn( str( node.name ) + " leaders output: \n" +
+                               str( response ) )
+
+        utilities.assert_equals( expect=main.TRUE, actual=topicCheck,
+                                 onpass="intent Partitions is in leaders",
+                                 onfail="Some topics were lost " )
+        # Print partitions
+        partitions = onosCli.partitions()
+        try:
+            if partitions :
+                parsedPartitions = json.loads( partitions )
+                main.log.warn( json.dumps( parsedPartitions,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check for a leader in all paritions
+                # TODO check for consistency among nodes
+            else:
+                main.log.error( "partitions() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing partitions" )
+            main.log.error( repr( partitions ) )
+        # Print Pending Map
+        pendingMap = onosCli.pendingMap()
+        try:
+            if pendingMap :
+                parsedPending = json.loads( pendingMap )
+                main.log.warn( json.dumps( parsedPending,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check something here?
+            else:
+                main.log.error( "pendingMap() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing pending map" )
+            main.log.error( repr( pendingMap ) )
+
+        if not installedCheck:
+            main.log.info( "Waiting 60 seconds to see if the state of " +
+                           "intents change" )
+            time.sleep( 60 )
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( intents ):
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents." )
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            leaders = onosCli.leaders()
+            try:
+                missing = False
+                if leaders:
+                    parsedLeaders = json.loads( leaders )
+                    main.log.warn( json.dumps( parsedLeaders,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # check for all intent partitions
+                    # check for election
+                    topics = []
+                    for i in range( 14 ):
+                        topics.append( "intent-partition-" + str( i ) )
+                    # FIXME: this should only be after we start the app
+                    topics.append( "org.onosproject.election" )
+                    main.log.debug( topics )
+                    ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                    for topic in topics:
+                        if topic not in ONOStopics:
+                            main.log.error( "Error: " + topic +
+                                            " not in leaders" )
+                            missing = True
+                else:
+                    main.log.error( "leaders() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing leaders" )
+                main.log.error( repr( leaders ) )
+            if missing:
+                for i in main.activeNodes:
+                    node = main.CLIs[i]
+                    response = node.leaders( jsonFormat=False)
+                    main.log.warn( str( node.name ) + " leaders output: \n" +
+                                   str( response ) )
+
+            partitions = onosCli.partitions()
+            try:
+                if partitions :
+                    parsedPartitions = json.loads( partitions )
+                    main.log.warn( json.dumps( parsedPartitions,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check for a leader in all paritions
+                    # TODO check for consistency among nodes
+                else:
+                    main.log.error( "partitions() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing partitions" )
+                main.log.error( repr( partitions ) )
+            pendingMap = onosCli.pendingMap()
+            try:
+                if pendingMap :
+                    parsedPending = json.loads( pendingMap )
+                    main.log.warn( json.dumps( parsedPending,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check something here?
+                else:
+                    main.log.error( "pendingMap() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing pending map" )
+                main.log.error( repr( pendingMap ) )
+        # Print flowrules
+        node = main.activeNodes[0]
+        main.log.debug( main.CLIs[node].flows( jsonFormat=False ) )
+        main.step( "Wait a minute then ping again" )
+        # the wait is above
+        PingResult = main.TRUE
+        for i in range( 8, 18 ):
+            ping = main.Mininet1.pingHost( src="h" + str( i ),
+                                           target="h" + str( i + 10 ) )
+            PingResult = PingResult and ping
+            if ping == main.FALSE:
+                main.log.warn( "Ping failed between h" + str( i ) +
+                               " and h" + str( i + 10 ) )
+            elif ping == main.TRUE:
+                main.log.info( "Ping test passed!" )
+                # Don't set PingResult or you'd override failures
+        if PingResult == main.FALSE:
+            main.log.error(
+                "Intents have not been installed correctly, pings failed." )
+            # TODO: pretty print
+            main.log.warn( "ONOS1 intents: " )
+            try:
+                tmpIntents = onosCli.intents()
+                main.log.warn( json.dumps( json.loads( tmpIntents ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+            except ( ValueError, TypeError ):
+                main.log.warn( repr( tmpIntents ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=PingResult,
+            onpass="Intents have been installed correctly and pings work",
+            onfail="Intents have not been installed correctly, pings failed." )
+
+    def CASE5( self, main ):
+        """
+        Reading state of ONOS
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Setting up and gathering data for current state" )
+        # The general idea for this test case is to pull the state of
+        # ( intents,flows, topology,... ) from each ONOS node
+        # We can then compare them with each other and also with past states
+
+        main.step( "Check that each switch has a master" )
+        global mastershipState
+        mastershipState = '[]'
+
+        # Assert that each device has a master
+        rolesNotNull = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].rolesNotNull,
+                             name="rolesNotNull-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            rolesNotNull = rolesNotNull and t.result
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=rolesNotNull,
+            onpass="Each device has a master",
+            onfail="Some devices don't have a master assigned" )
+
+        main.step( "Get the Mastership of each switch from each controller" )
+        ONOSMastership = []
+        consistentMastership = True
+        rolesResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].roles,
+                             name="roles-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSMastership.append( t.result )
+
+        for i in range( len( ONOSMastership ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSMastership[i] or "Error" in ONOSMastership[i]:
+                main.log.error( "Error in getting ONOS" + node + " roles" )
+                main.log.warn( "ONOS" + node + " mastership response: " +
+                               repr( ONOSMastership[i] ) )
+                rolesResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=rolesResults,
+            onpass="No error in reading roles output",
+            onfail="Error in reading roles from ONOS" )
+
+        main.step( "Check for consistency in roles from each controller" )
+        if all([ i == ONOSMastership[ 0 ] for i in ONOSMastership ] ):
+            main.log.info(
+                "Switch roles are consistent across all ONOS nodes" )
+        else:
+            consistentMastership = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentMastership,
+            onpass="Switch roles are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of switch roles" )
+
+        if rolesResults and not consistentMastership:
+            for i in range( len( main.activeNodes ) ):
+                node = str( main.activeNodes[i] + 1 )
+                try:
+                    main.log.warn(
+                        "ONOS" + node + " roles: ",
+                        json.dumps(
+                            json.loads( ONOSMastership[ i ] ),
+                            sort_keys=True,
+                            indent=4,
+                            separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( repr( ONOSMastership[ i ] ) )
+        elif rolesResults and consistentMastership:
+            mastershipState = ONOSMastership[ 0 ]
+
+        main.step( "Get the intents from each controller" )
+        global intentState
+        intentState = []
+        ONOSIntents = []
+        consistentIntents = True  # Are Intents consistent across nodes?
+        intentsResults = True  # Could we read Intents from ONOS?
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].intents,
+                             name="intents-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSIntents.append( t.result )
+
+        for i in range( len( ONOSIntents ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSIntents[ i ] or "Error" in ONOSIntents[ i ]:
+                main.log.error( "Error in getting ONOS" + node + " intents" )
+                main.log.warn( "ONOS" + node + " intents response: " +
+                               repr( ONOSIntents[ i ] ) )
+                intentsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=intentsResults,
+            onpass="No error in reading intents output",
+            onfail="Error in reading intents from ONOS" )
+
+        main.step( "Check for consistency in Intents from each controller" )
+        if all([ sorted( i ) == sorted( ONOSIntents[ 0 ] ) for i in ONOSIntents ] ):
+            main.log.info( "Intents are consistent across all ONOS " +
+                             "nodes" )
+        else:
+            consistentIntents = False
+            main.log.error( "Intents not consistent" )
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentIntents,
+            onpass="Intents are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of intents" )
+
+        if intentsResults:
+            # Try to make it easy to figure out what is happening
+            #
+            # Intent      ONOS1      ONOS2    ...
+            #  0x01     INSTALLED  INSTALLING
+            #  ...        ...         ...
+            #  ...        ...         ...
+            title = "   Id"
+            for n in main.activeNodes:
+                title += " " * 10 + "ONOS" + str( n + 1 )
+            main.log.warn( title )
+            # get all intent keys in the cluster
+            keys = []
+            try:
+                # Get the set of all intent keys
+                for nodeStr in ONOSIntents:
+                    node = json.loads( nodeStr )
+                    for intent in node:
+                        keys.append( intent.get( 'id' ) )
+                keys = set( keys )
+                # For each intent key, print the state on each node
+                for key in keys:
+                    row = "%-13s" % key
+                    for nodeStr in ONOSIntents:
+                        node = json.loads( nodeStr )
+                        for intent in node:
+                            if intent.get( 'id', "Error" ) == key:
+                                row += "%-15s" % intent.get( 'state' )
+                    main.log.warn( row )
+                # End of intent state table
+            except ValueError as e:
+                main.log.exception( e )
+                main.log.debug( "nodeStr was: " + repr( nodeStr ) )
+
+        if intentsResults and not consistentIntents:
+            # print the json objects
+            n = str( main.activeNodes[-1] + 1 )
+            main.log.debug( "ONOS" + n + " intents: " )
+            main.log.debug( json.dumps( json.loads( ONOSIntents[ -1 ] ),
+                                        sort_keys=True,
+                                        indent=4,
+                                        separators=( ',', ': ' ) ) )
+            for i in range( len( ONOSIntents ) ):
+                node = str( main.activeNodes[i] + 1 )
+                if ONOSIntents[ i ] != ONOSIntents[ -1 ]:
+                    main.log.debug( "ONOS" + node + " intents: " )
+                    main.log.debug( json.dumps( json.loads( ONOSIntents[i] ),
+                                                sort_keys=True,
+                                                indent=4,
+                                                separators=( ',', ': ' ) ) )
+                else:
+                    main.log.debug( "ONOS" + node + " intents match ONOS" +
+                                    n + " intents" )
+        elif intentsResults and consistentIntents:
+            intentState = ONOSIntents[ 0 ]
+
+        main.step( "Get the flows from each controller" )
+        global flowState
+        flowState = []
+        ONOSFlows = []
+        ONOSFlowsJson = []
+        flowCheck = main.FALSE
+        consistentFlows = True
+        flowsResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].flows,
+                             name="flows-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        # NOTE: Flows command can take some time to run
+        time.sleep(30)
+        for t in threads:
+            t.join()
+            result = t.result
+            ONOSFlows.append( result )
+
+        for i in range( len( ONOSFlows ) ):
+            num = str( main.activeNodes[i] + 1 )
+            if not ONOSFlows[ i ] or "Error" in ONOSFlows[ i ]:
+                main.log.error( "Error in getting ONOS" + num + " flows" )
+                main.log.warn( "ONOS" + num + " flows response: " +
+                               repr( ONOSFlows[ i ] ) )
+                flowsResults = False
+                ONOSFlowsJson.append( None )
+            else:
+                try:
+                    ONOSFlowsJson.append( json.loads( ONOSFlows[ i ] ) )
+                except ( ValueError, TypeError ):
+                    # FIXME: change this to log.error?
+                    main.log.exception( "Error in parsing ONOS" + num +
+                                        " response as json." )
+                    main.log.error( repr( ONOSFlows[ i ] ) )
+                    ONOSFlowsJson.append( None )
+                    flowsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=flowsResults,
+            onpass="No error in reading flows output",
+            onfail="Error in reading flows from ONOS" )
+
+        main.step( "Check for consistency in Flows from each controller" )
+        tmp = [ len( i ) == len( ONOSFlowsJson[ 0 ] ) for i in ONOSFlowsJson ]
+        if all( tmp ):
+            main.log.info( "Flow count is consistent across all ONOS nodes" )
+        else:
+            consistentFlows = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentFlows,
+            onpass="The flow count is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different flow counts" )
+
+        if flowsResults and not consistentFlows:
+            for i in range( len( ONOSFlows ) ):
+                node = str( main.activeNodes[i] + 1 )
+                try:
+                    main.log.warn(
+                        "ONOS" + node + " flows: " +
+                        json.dumps( json.loads( ONOSFlows[i] ), sort_keys=True,
+                                    indent=4, separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( "ONOS" + node + " flows: " +
+                                   repr( ONOSFlows[ i ] ) )
+        elif flowsResults and consistentFlows:
+            flowCheck = main.TRUE
+            flowState = ONOSFlows[ 0 ]
+
+        main.step( "Get the OF Table entries" )
+        global flows
+        flows = []
+        for i in range( 1, 29 ):
+            flows.append( main.Mininet1.getFlowTable( "s" + str( i ), version="1.3", debug=False ) )
+        if flowCheck == main.FALSE:
+            for table in flows:
+                main.log.warn( table )
+        # TODO: Compare switch flow tables with ONOS flow tables
+
+        main.step( "Start continuous pings" )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source1' ],
+            target=main.params[ 'PING' ][ 'target1' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source2' ],
+            target=main.params[ 'PING' ][ 'target2' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source3' ],
+            target=main.params[ 'PING' ][ 'target3' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source4' ],
+            target=main.params[ 'PING' ][ 'target4' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source5' ],
+            target=main.params[ 'PING' ][ 'target5' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source6' ],
+            target=main.params[ 'PING' ][ 'target6' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source7' ],
+            target=main.params[ 'PING' ][ 'target7' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source8' ],
+            target=main.params[ 'PING' ][ 'target8' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source9' ],
+            target=main.params[ 'PING' ][ 'target9' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source10' ],
+            target=main.params[ 'PING' ][ 'target10' ],
+            pingTime=500 )
+
+        main.step( "Collecting topology information from ONOS" )
+        devices = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        hosts = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].hosts,
+                             name="hosts-" + str( i ),
+                             args=[ ] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            try:
+                hosts.append( json.loads( t.result ) )
+            except ( ValueError, TypeError ):
+                # FIXME: better handling of this, print which node
+                #        Maybe use thread name?
+                main.log.exception( "Error parsing json output of hosts" )
+                main.log.warn( repr( t.result ) )
+                hosts.append( None )
+
+        ports = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        links = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        clusters = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        # Compare json objects for hosts and dataplane clusters
+
+        # hosts
+        main.step( "Host view is consistent across ONOS nodes" )
+        consistentHostsResult = main.TRUE
+        for controller in range( len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ] and "Error" not in hosts[ controller ]:
+                if hosts[ controller ] == hosts[ 0 ]:
+                    continue
+                else:  # hosts not consistent
+                    main.log.error( "hosts from ONOS" +
+                                     controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    main.log.warn( repr( hosts[ controller ] ) )
+                    consistentHostsResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting ONOS hosts from ONOS" +
+                                 controllerStr )
+                consistentHostsResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " hosts response: " +
+                               repr( hosts[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentHostsResult,
+            onpass="Hosts view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of hosts" )
+
+        main.step( "Each host has an IP address" )
+        ipResult = main.TRUE
+        for controller in range( 0, len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ]:
+                for host in hosts[ controller ]:
+                    if not host.get( 'ipAddresses', [ ] ):
+                        main.log.error( "Error with host ips on controller" +
+                                        controllerStr + ": " + str( host ) )
+                        ipResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=ipResult,
+            onpass="The ips of the hosts aren't empty",
+            onfail="The ip of at least one host is missing" )
+
+        # Strongly connected clusters of devices
+        main.step( "Cluster view is consistent across ONOS nodes" )
+        consistentClustersResult = main.TRUE
+        for controller in range( len( clusters ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if "Error" not in clusters[ controller ]:
+                if clusters[ controller ] == clusters[ 0 ]:
+                    continue
+                else:  # clusters not consistent
+                    main.log.error( "clusters from ONOS" + controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    consistentClustersResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting dataplane clusters " +
+                                 "from ONOS" + controllerStr )
+                consistentClustersResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " clusters response: " +
+                               repr( clusters[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentClustersResult,
+            onpass="Clusters view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
+
+        # there should always only be one cluster
+        main.step( "Cluster view correct across ONOS nodes" )
+        try:
+            numClusters = len( json.loads( clusters[ 0 ] ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing clusters[0]: " +
+                                repr( clusters[ 0 ] ) )
+            numClusters = "ERROR"
+        utilities.assert_equals(
+            expect=1,
+            actual=numClusters,
+            onpass="ONOS shows 1 SCC",
+            onfail="ONOS shows " + str( numClusters ) + " SCCs" )
+
+        main.step( "Comparing ONOS topology to MN" )
+        devicesResults = main.TRUE
+        linksResults = main.TRUE
+        hostsResults = main.TRUE
+        mnSwitches = main.Mininet1.getSwitches()
+        mnLinks = main.Mininet1.getLinks()
+        mnHosts = main.Mininet1.getHosts()
+        for controller in main.activeNodes:
+            controllerStr = str( main.activeNodes[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 ] and "Error" not in hosts[ controller ]:
+                currentHostsResult = main.Mininet1.compareHosts(
+                        mnHosts,
+                        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" )
+
+            devicesResults = devicesResults and currentDevicesResult
+            linksResults = linksResults and currentLinksResult
+            hostsResults = hostsResults and currentHostsResult
+
+        main.step( "Device information is correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=devicesResults,
+            onpass="Device information is correct",
+            onfail="Device information is incorrect" )
+
+        main.step( "Links are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=linksResults,
+            onpass="Link are correct",
+            onfail="Links are incorrect" )
+
+        main.step( "Hosts are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Hosts are correct",
+            onfail="Hosts are incorrect" )
+
+    def CASE6( self, main ):
+        """
+        The Scaling case.
+        """
+        import time
+        import re
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        try:
+            labels
+        except NameError:
+            main.log.error( "labels not defined, setting to []" )
+            global labels
+            labels = []
+        try:
+            data
+        except NameError:
+            main.log.error( "data not defined, setting to []" )
+            global data
+            data = []
+
+        main.case( "Scale the number of nodes in the ONOS cluster" )
+
+        main.step( "Checking ONOS Logs for errors" )
+        for i in main.activeNodes:
+            node = main.nodes[i]
+            main.log.debug( "Checking logs for errors on " + node.name + ":" )
+            main.log.warn( main.ONOSbench.checkLogs( node.ip_address ) )
+
+        """
+        pop # of nodes from a list, might look like 1,3b,3,5b,5,7b,7,7b,5,5b,3...
+        modify cluster.json file appropriately
+        install/deactivate node as needed
+        """
+
+        try:
+            prevNodes = main.activeNodes
+            scale = main.scaling.pop(0)
+            if "e" in scale:
+                equal = True
+            else:
+                equal = False
+            main.numCtrls = int( re.search( "\d+", scale ).group(0) )
+            main.log.info( "Scaling to {} nodes".format( main.numCtrls ) )
+            genResult = main.Server.generateFile( main.numCtrls, equal=equal )
+            utilities.assert_equals( expect=main.TRUE, actual=genResult,
+                                     onpass="New cluster metadata file generated",
+                                     onfail="Failled to generate new metadata file" )
+            time.sleep( 5 )  # Give time for nodes to read new file
+        except IndexError:
+            main.cleanup()
+            main.exit()
+
+        main.activeNodes = [ i for i in range( 0, main.numCtrls ) ]
+        newNodes = [ x for x in main.activeNodes if x not in prevNodes ]
+
+        main.step( "Start new nodes" )  # OR stop old nodes?
+        started = main.TRUE
+        for i in newNodes:
+            started = main.ONOSbench.onosStart( main.nodes[i].ip_address ) and main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=started,
+                                 onpass="ONOS started",
+                                 onfail="ONOS start NOT successful" )
+
+        main.step( "Checking if ONOS is up yet" )
+        for i in range( 2 ):
+            onosIsupResult = main.TRUE
+            for i in main.activeNodes:
+                node = main.nodes[i]
+                started = main.ONOSbench.isup( node.ip_address )
+                if not started:
+                    main.log.error( node.name + " didn't start!" )
+                onosIsupResult = onosIsupResult and started
+            if onosIsupResult == main.TRUE:
+                break
+        utilities.assert_equals( expect=main.TRUE, actual=onosIsupResult,
+                                 onpass="ONOS started",
+                                 onfail="ONOS start NOT successful" )
+
+        main.log.step( "Starting ONOS CLI sessions" )
+        cliResults = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].startOnosCli,
+                             name="startOnosCli-" + str( i ),
+                             args=[main.nodes[i].ip_address] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            cliResults = cliResults and t.result
+        utilities.assert_equals( expect=main.TRUE, actual=cliResults,
+                                 onpass="ONOS cli started",
+                                 onfail="ONOS clis did not start" )
+
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+
+        for i in range( 10 ):
+            ready = True
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
+                output = cli.summary()
+                if not output:
+                    ready = False
+            if ready:
+                break
+            time.sleep( 30 )
+        utilities.assert_equals( expect=True, actual=ready,
+                                 onpass="ONOS summary command succeded",
+                                 onfail="ONOS summary command failed" )
+        if not ready:
+            main.cleanup()
+            main.exit()
+
+        # Rerun for election on new nodes
+        runResults = main.TRUE
+        for i in main.activeNodes:
+            cli = main.CLIs[i]
+            run = cli.electionTestRun()
+            if run != main.TRUE:
+                main.log.error( "Error running for election on " + cli.name )
+            runResults = runResults and run
+        utilities.assert_equals( expect=main.TRUE, actual=runResults,
+                                 onpass="Reran for election",
+                                 onfail="Failed to rerun for election" )
+
+        # TODO: Make this configurable
+        time.sleep( 60 )
+        for node in main.activeNodes:
+            main.log.warn( "\n****************** {} **************".format( main.nodes[node].ip_address ) )
+            main.log.debug( main.CLIs[node].nodes( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].leaders( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].partitions( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].apps( jsonFormat=False ) )
+
+    def CASE7( self, main ):
+        """
+        Check state after ONOS scaling
+        """
+        import json
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        main.case( "Running ONOS Constant State Tests" )
+
+        main.step( "Check that each switch has a master" )
+        # Assert that each device has a master
+        rolesNotNull = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].rolesNotNull,
+                             name="rolesNotNull-" + str( i ),
+                             args=[ ] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            rolesNotNull = rolesNotNull and t.result
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=rolesNotNull,
+            onpass="Each device has a master",
+            onfail="Some devices don't have a master assigned" )
+
+        main.step( "Read device roles from ONOS" )
+        ONOSMastership = []
+        consistentMastership = True
+        rolesResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].roles,
+                             name="roles-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSMastership.append( t.result )
+
+        for i in range( len( ONOSMastership ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSMastership[i] or "Error" in ONOSMastership[i]:
+                main.log.error( "Error in getting ONOS" + node + " roles" )
+                main.log.warn( "ONOS" + node + " mastership response: " +
+                               repr( ONOSMastership[i] ) )
+                rolesResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=rolesResults,
+            onpass="No error in reading roles output",
+            onfail="Error in reading roles from ONOS" )
+
+        main.step( "Check for consistency in roles from each controller" )
+        if all([ i == ONOSMastership[ 0 ] for i in ONOSMastership ] ):
+            main.log.info(
+                "Switch roles are consistent across all ONOS nodes" )
+        else:
+            consistentMastership = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentMastership,
+            onpass="Switch roles are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of switch roles" )
+
+        if rolesResults and not consistentMastership:
+            for i in range( len( ONOSMastership ) ):
+                node = str( main.activeNodes[i] + 1 )
+                main.log.warn( "ONOS" + node + " roles: ",
+                               json.dumps( json.loads( ONOSMastership[ i ] ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+
+        # NOTE: we expect mastership to change on controller scaling down
+
+        main.step( "Get the intents and compare across all nodes" )
+        ONOSIntents = []
+        intentCheck = main.FALSE
+        consistentIntents = True
+        intentsResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].intents,
+                             name="intents-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSIntents.append( t.result )
+
+        for i in range( len( ONOSIntents) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSIntents[ i ] or "Error" in ONOSIntents[ i ]:
+                main.log.error( "Error in getting ONOS" + node + " intents" )
+                main.log.warn( "ONOS" + node + " intents response: " +
+                               repr( ONOSIntents[ i ] ) )
+                intentsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=intentsResults,
+            onpass="No error in reading intents output",
+            onfail="Error in reading intents from ONOS" )
+
+        main.step( "Check for consistency in Intents from each controller" )
+        if all([ sorted( i ) == sorted( ONOSIntents[ 0 ] ) for i in ONOSIntents ] ):
+            main.log.info( "Intents are consistent across all ONOS " +
+                             "nodes" )
+        else:
+            consistentIntents = False
+
+        # Try to make it easy to figure out what is happening
+        #
+        # Intent      ONOS1      ONOS2    ...
+        #  0x01     INSTALLED  INSTALLING
+        #  ...        ...         ...
+        #  ...        ...         ...
+        title = "   ID"
+        for n in main.activeNodes:
+            title += " " * 10 + "ONOS" + str( n + 1 )
+        main.log.warn( title )
+        # get all intent keys in the cluster
+        keys = []
+        for nodeStr in ONOSIntents:
+            node = json.loads( nodeStr )
+            for intent in node:
+                keys.append( intent.get( 'id' ) )
+        keys = set( keys )
+        for key in keys:
+            row = "%-13s" % key
+            for nodeStr in ONOSIntents:
+                node = json.loads( nodeStr )
+                for intent in node:
+                    if intent.get( 'id' ) == key:
+                        row += "%-15s" % intent.get( 'state' )
+            main.log.warn( row )
+        # End table view
+
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentIntents,
+            onpass="Intents are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of intents" )
+        intentStates = []
+        for node in ONOSIntents:  # Iter through ONOS nodes
+            nodeStates = []
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( node ):
+                    nodeStates.append( intent[ 'state' ] )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error in parsing intents" )
+                main.log.error( repr( node ) )
+            intentStates.append( nodeStates )
+            out = [ (i, nodeStates.count( i ) ) for i in set( nodeStates ) ]
+            main.log.info( dict( out ) )
+
+        if intentsResults and not consistentIntents:
+            for i in range( len( main.activeNodes ) ):
+                node = str( main.activeNodes[i] + 1 )
+                main.log.warn( "ONOS" + node + " intents: " )
+                main.log.warn( json.dumps(
+                    json.loads( ONOSIntents[ i ] ),
+                    sort_keys=True,
+                    indent=4,
+                    separators=( ',', ': ' ) ) )
+        elif intentsResults and consistentIntents:
+            intentCheck = main.TRUE
+
+        main.step( "Compare current intents with intents before the scaling" )
+        # NOTE: this requires case 5 to pass for intentState to be set.
+        #      maybe we should stop the test if that fails?
+        sameIntents = main.FALSE
+        try:
+            intentState
+        except NameError:
+            main.log.warn( "No previous intent state was saved" )
+        else:
+            if intentState and intentState == ONOSIntents[ 0 ]:
+                sameIntents = main.TRUE
+                main.log.info( "Intents are consistent with before scaling" )
+            # TODO: possibly the states have changed? we may need to figure out
+            #       what the acceptable states are
+            elif len( intentState ) == len( ONOSIntents[ 0 ] ):
+                sameIntents = main.TRUE
+                try:
+                    before = json.loads( intentState )
+                    after = json.loads( ONOSIntents[ 0 ] )
+                    for intent in before:
+                        if intent not in after:
+                            sameIntents = main.FALSE
+                            main.log.debug( "Intent is not currently in ONOS " +
+                                            "(at least in the same form):" )
+                            main.log.debug( json.dumps( intent ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Exception printing intents" )
+                    main.log.debug( repr( ONOSIntents[0] ) )
+                    main.log.debug( repr( intentState ) )
+            if sameIntents == main.FALSE:
+                try:
+                    main.log.debug( "ONOS intents before: " )
+                    main.log.debug( json.dumps( json.loads( intentState ),
+                                                sort_keys=True, indent=4,
+                                                separators=( ',', ': ' ) ) )
+                    main.log.debug( "Current ONOS intents: " )
+                    main.log.debug( json.dumps( json.loads( ONOSIntents[ 0 ] ),
+                                                sort_keys=True, indent=4,
+                                                separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Exception printing intents" )
+                    main.log.debug( repr( ONOSIntents[0] ) )
+                    main.log.debug( repr( intentState ) )
+            utilities.assert_equals(
+                expect=main.TRUE,
+                actual=sameIntents,
+                onpass="Intents are consistent with before scaling",
+                onfail="The Intents changed during scaling" )
+        intentCheck = intentCheck and sameIntents
+
+        main.step( "Get the OF Table entries and compare to before " +
+                   "component scaling" )
+        FlowTables = main.TRUE
+        for i in range( 28 ):
+            main.log.info( "Checking flow table on s" + str( i + 1 ) )
+            tmpFlows = main.Mininet1.getFlowTable( "s" + str( i + 1 ), version="1.3", debug=False )
+            curSwitch = main.Mininet1.flowTableComp( flows[i], tmpFlows )
+            FlowTables = FlowTables and curSwitch
+            if curSwitch == main.FALSE:
+                main.log.warn( "Differences in flow table for switch: s{}".format( i + 1 ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=FlowTables,
+            onpass="No changes were found in the flow tables",
+            onfail="Changes were found in the flow tables" )
+
+        main.Mininet2.pingLongKill()
+        '''
+        # main.step( "Check the continuous pings to ensure that no packets " +
+        #            "were dropped during component failure" )
+        main.Mininet2.pingKill( main.params[ 'TESTONUSER' ],
+                                main.params[ 'TESTONIP' ] )
+        LossInPings = main.FALSE
+        # NOTE: checkForLoss returns main.FALSE with 0% packet loss
+        for i in range( 8, 18 ):
+            main.log.info(
+                "Checking for a loss in pings along flow from s" +
+                str( i ) )
+            LossInPings = main.Mininet2.checkForLoss(
+                "/tmp/ping.h" +
+                str( i ) ) or LossInPings
+        if LossInPings == main.TRUE:
+            main.log.info( "Loss in ping detected" )
+        elif LossInPings == main.ERROR:
+            main.log.info( "There are multiple mininet process running" )
+        elif LossInPings == main.FALSE:
+            main.log.info( "No Loss in the pings" )
+            main.log.info( "No loss of dataplane connectivity" )
+        # utilities.assert_equals(
+        #     expect=main.FALSE,
+        #     actual=LossInPings,
+        #     onpass="No Loss of connectivity",
+        #     onfail="Loss of dataplane connectivity detected" )
+
+        # NOTE: Since intents are not persisted with IntnentStore,
+        #       we expect loss in dataplane connectivity
+        LossInPings = main.FALSE
+        '''
+
+        main.step( "Leadership Election is still functional" )
+        # Test of LeadershipElection
+        leaderList = []
+        leaderResult = main.TRUE
+
+        for i in main.activeNodes:
+            cli = main.CLIs[i]
+            leaderN = cli.electionTestLeader()
+            leaderList.append( leaderN )
+            if leaderN == main.FALSE:
+                # error in response
+                main.log.error( "Something is wrong with " +
+                                 "electionTestLeader function, check the" +
+                                 " error logs" )
+                leaderResult = main.FALSE
+            elif leaderN is None:
+                main.log.error( cli.name +
+                                 " shows no leader for the election-app." )
+                leaderResult = main.FALSE
+        if len( set( leaderList ) ) != 1:
+            leaderResult = main.FALSE
+            main.log.error(
+                "Inconsistent view of leader for the election test app" )
+            # TODO: print the list
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=leaderResult,
+            onpass="Leadership election passed",
+            onfail="Something went wrong with Leadership election" )
+
+    def CASE8( self, main ):
+        """
+        Compare topo
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Compare ONOS Topology view to Mininet topology" )
+        main.caseExplanation = "Compare topology objects between Mininet" +\
+                                " and ONOS"
+        topoResult = main.FALSE
+        topoFailMsg = "ONOS topology don't match Mininet"
+        elapsed = 0
+        count = 0
+        main.step( "Comparing ONOS topology to MN topology" )
+        startTime = time.time()
+        # Give time for Gossip to work
+        while topoResult == main.FALSE and ( elapsed < 60 or count < 3 ):
+            devicesResults = main.TRUE
+            linksResults = main.TRUE
+            hostsResults = main.TRUE
+            hostAttachmentResults = True
+            count += 1
+            cliStart = time.time()
+            devices = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="devices-" + str( i ),
+                                 args=[ main.CLIs[i].devices, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                devices.append( t.result )
+            hosts = []
+            ipResult = main.TRUE
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="hosts-" + str( i ),
+                                 args=[ main.CLIs[i].hosts, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                try:
+                    hosts.append( json.loads( t.result ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Error parsing hosts results" )
+                    main.log.error( repr( t.result ) )
+                    hosts.append( None )
+            for controller in range( 0, len( hosts ) ):
+                controllerStr = str( main.activeNodes[controller] + 1 )
+                if hosts[ controller ]:
+                    for host in hosts[ controller ]:
+                        if host is None or host.get( 'ipAddresses', [] ) == []:
+                            main.log.error(
+                                "Error with host ipAddresses on controller" +
+                                controllerStr + ": " + str( host ) )
+                            ipResult = main.FALSE
+            ports = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="ports-" + str( i ),
+                                 args=[ main.CLIs[i].ports, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                ports.append( t.result )
+            links = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="links-" + str( i ),
+                                 args=[ main.CLIs[i].links, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                links.append( t.result )
+            clusters = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="clusters-" + str( i ),
+                                 args=[ main.CLIs[i].clusters, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                clusters.append( t.result )
+
+            elapsed = time.time() - startTime
+            cliTime = time.time() - cliStart
+            print "Elapsed time: " + str( elapsed )
+            print "CLI time: " + str( cliTime )
+
+            if all( e is None for e in devices ) and\
+               all( e is None for e in hosts ) and\
+               all( e is None for e in ports ) and\
+               all( e is None for e in links ) and\
+               all( e is None for e in clusters ):
+                   topoFailMsg = "Could not get topology from ONOS"
+                   main.log.error( topoFailMsg )
+                   continue  # Try again, No use trying to compare
+
+            mnSwitches = main.Mininet1.getSwitches()
+            mnLinks = main.Mininet1.getLinks()
+            mnHosts = main.Mininet1.getHosts()
+            for controller in range( len( main.activeNodes ) ):
+                controllerStr = str( main.activeNodes[controller] + 1 )
+                if devices[ controller ] and ports[ controller ] and\
+                    "Error" not in devices[ controller ] and\
+                    "Error" not in ports[ controller ]:
+
+                    try:
+                        currentDevicesResult = main.Mininet1.compareSwitches(
+                                mnSwitches,
+                                json.loads( devices[ controller ] ),
+                                json.loads( ports[ controller ] ) )
+                    except ( TypeError, ValueError ):
+                        main.log.exception( "Object not as expected; devices={!r}\nports={!r}".format(
+                            devices[ controller ], 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 ] and "Error" not in hosts[ controller ]:
+                    currentHostsResult = main.Mininet1.compareHosts(
+                            mnHosts,
+                            hosts[ controller ] )
+                elif hosts[ controller ] == []:
+                    currentHostsResult = main.TRUE
+                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" )
+                # CHECKING HOST ATTACHMENT POINTS
+                hostAttachment = True
+                zeroHosts = False
+                # FIXME: topo-HA/obelisk specific mappings:
+                # key is mac and value is dpid
+                mappings = {}
+                for i in range( 1, 29 ):  # hosts 1 through 28
+                    # set up correct variables:
+                    macId = "00:" * 5 + hex( i ).split( "0x" )[1].upper().zfill(2)
+                    if i == 1:
+                        deviceId = "1000".zfill(16)
+                    elif i == 2:
+                        deviceId = "2000".zfill(16)
+                    elif i == 3:
+                        deviceId = "3000".zfill(16)
+                    elif i == 4:
+                        deviceId = "3004".zfill(16)
+                    elif i == 5:
+                        deviceId = "5000".zfill(16)
+                    elif i == 6:
+                        deviceId = "6000".zfill(16)
+                    elif i == 7:
+                        deviceId = "6007".zfill(16)
+                    elif i >= 8 and i <= 17:
+                        dpid = '3' + str( i ).zfill( 3 )
+                        deviceId = dpid.zfill(16)
+                    elif i >= 18 and i <= 27:
+                        dpid = '6' + str( i ).zfill( 3 )
+                        deviceId = dpid.zfill(16)
+                    elif i == 28:
+                        deviceId = "2800".zfill(16)
+                    mappings[ macId ] = deviceId
+                if hosts[ controller ] is not None and "Error" not in hosts[ controller ]:
+                    if hosts[ controller ] == []:
+                        main.log.warn( "There are no hosts discovered" )
+                        zeroHosts = True
+                    else:
+                        for host in hosts[ controller ]:
+                            mac = None
+                            location = None
+                            device = None
+                            port = None
+                            try:
+                                mac = host.get( 'mac' )
+                                assert mac, "mac field could not be found for this host object"
+
+                                location = host.get( 'location' )
+                                assert location, "location field could not be found for this host object"
+
+                                # Trim the protocol identifier off deviceId
+                                device = str( location.get( 'elementId' ) ).split(':')[1]
+                                assert device, "elementId field could not be found for this host location object"
+
+                                port = location.get( 'port' )
+                                assert port, "port field could not be found for this host location object"
+
+                                # Now check if this matches where they should be
+                                if mac and device and port:
+                                    if str( port ) != "1":
+                                        main.log.error( "The attachment port is incorrect for " +
+                                                        "host " + str( mac ) +
+                                                        ". Expected: 1 Actual: " + str( port) )
+                                        hostAttachment = False
+                                    if device != mappings[ str( mac ) ]:
+                                        main.log.error( "The attachment device is incorrect for " +
+                                                        "host " + str( mac ) +
+                                                        ". Expected: " + mappings[ str( mac ) ] +
+                                                        " Actual: " + device )
+                                        hostAttachment = False
+                                else:
+                                    hostAttachment = False
+                            except AssertionError:
+                                main.log.exception( "Json object not as expected" )
+                                main.log.error( repr( host ) )
+                                hostAttachment = False
+                else:
+                    main.log.error( "No hosts json output or \"Error\"" +
+                                    " in output. hosts = " +
+                                    repr( hosts[ controller ] ) )
+                if zeroHosts is False:
+                    # TODO: Find a way to know if there should be hosts in a
+                    #       given point of the test
+                    hostAttachment = True
+
+                # END CHECKING HOST ATTACHMENT POINTS
+                devicesResults = devicesResults and currentDevicesResult
+                linksResults = linksResults and currentLinksResult
+                hostsResults = hostsResults and currentHostsResult
+                hostAttachmentResults = hostAttachmentResults and\
+                                        hostAttachment
+                topoResult = ( devicesResults and linksResults
+                               and hostsResults and ipResult and
+                               hostAttachmentResults )
+        utilities.assert_equals( expect=True,
+                                 actual=topoResult,
+                                 onpass="ONOS topology matches Mininet",
+                                 onfail=topoFailMsg )
+        # End of While loop to pull ONOS state
+
+        # Compare json objects for hosts and dataplane clusters
+
+        # hosts
+        main.step( "Hosts view is consistent across all ONOS nodes" )
+        consistentHostsResult = main.TRUE
+        for controller in range( len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ] is not None and "Error" not in hosts[ controller ]:
+                if hosts[ controller ] == hosts[ 0 ]:
+                    continue
+                else:  # hosts not consistent
+                    main.log.error( "hosts from ONOS" + controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    main.log.warn( repr( hosts[ controller ] ) )
+                    consistentHostsResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting ONOS hosts from ONOS" +
+                                 controllerStr )
+                consistentHostsResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " hosts response: " +
+                               repr( hosts[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentHostsResult,
+            onpass="Hosts view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of hosts" )
+
+        main.step( "Hosts information is correct" )
+        hostsResults = hostsResults and ipResult
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Host information is correct",
+            onfail="Host information is incorrect" )
+
+        main.step( "Host attachment points to the network" )
+        utilities.assert_equals(
+            expect=True,
+            actual=hostAttachmentResults,
+            onpass="Hosts are correctly attached to the network",
+            onfail="ONOS did not correctly attach hosts to the network" )
+
+        # Strongly connected clusters of devices
+        main.step( "Clusters view is consistent across all ONOS nodes" )
+        consistentClustersResult = main.TRUE
+        for controller in range( len( clusters ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if "Error" not in clusters[ controller ]:
+                if clusters[ controller ] == clusters[ 0 ]:
+                    continue
+                else:  # clusters not consistent
+                    main.log.error( "clusters from ONOS" +
+                                     controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    consistentClustersResult = main.FALSE
+            else:
+                main.log.error( "Error in getting dataplane clusters " +
+                                 "from ONOS" + controllerStr )
+                consistentClustersResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " clusters response: " +
+                               repr( clusters[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentClustersResult,
+            onpass="Clusters view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
+
+        main.step( "There is only one SCC" )
+        # there should always only be one cluster
+        try:
+            numClusters = len( json.loads( clusters[ 0 ] ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing clusters[0]: " +
+                                repr( clusters[0] ) )
+            numClusters = "ERROR"
+        clusterResults = main.FALSE
+        if numClusters == 1:
+            clusterResults = main.TRUE
+        utilities.assert_equals(
+            expect=1,
+            actual=numClusters,
+            onpass="ONOS shows 1 SCC",
+            onfail="ONOS shows " + str( numClusters ) + " SCCs" )
+
+        topoResult = ( devicesResults and linksResults
+                       and hostsResults and consistentHostsResult
+                       and consistentClustersResult and clusterResults
+                       and ipResult and hostAttachmentResults )
+
+        topoResult = topoResult and int( count <= 2 )
+        note = "note it takes about " + str( int( cliTime ) ) + \
+            " seconds for the test to make all the cli calls to fetch " +\
+            "the topology from each ONOS instance"
+        main.log.info(
+            "Very crass estimate for topology discovery/convergence( " +
+            str( note ) + " ): " + str( elapsed ) + " seconds, " +
+            str( count ) + " tries" )
+
+        main.step( "Device information is correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=devicesResults,
+            onpass="Device information is correct",
+            onfail="Device information is incorrect" )
+
+        main.step( "Links are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=linksResults,
+            onpass="Link are correct",
+            onfail="Links are incorrect" )
+
+        main.step( "Hosts are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Hosts are correct",
+            onfail="Hosts are incorrect" )
+
+        # FIXME: move this to an ONOS state case
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+        if not nodeResults:
+            for i in main.activeNodes:
+                main.log.debug( "{} components not ACTIVE: \n{}".format(
+                    main.CLIs[i].name,
+                    main.CLIs[i].sendline( "scr:list | grep -v ACTIVE" ) ) )
+
+    def CASE9( self, main ):
+        """
+        Link s3-s28 down
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        # NOTE: You should probably run a topology check after this
+
+        linkSleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
+
+        description = "Turn off a link to ensure that Link Discovery " +\
+                      "is working properly"
+        main.case( description )
+
+        main.step( "Kill Link between s3 and s28" )
+        LinkDown = main.Mininet1.link( END1="s3", END2="s28", OPTION="down" )
+        main.log.info( "Waiting " + str( linkSleep ) +
+                       " seconds for link down to be discovered" )
+        time.sleep( linkSleep )
+        utilities.assert_equals( expect=main.TRUE, actual=LinkDown,
+                                 onpass="Link down successful",
+                                 onfail="Failed to bring link down" )
+        # TODO do some sort of check here
+
+    def CASE10( self, main ):
+        """
+        Link s3-s28 up
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        # NOTE: You should probably run a topology check after this
+
+        linkSleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
+
+        description = "Restore a link to ensure that Link Discovery is " + \
+                      "working properly"
+        main.case( description )
+
+        main.step( "Bring link between s3 and s28 back up" )
+        LinkUp = main.Mininet1.link( END1="s3", END2="s28", OPTION="up" )
+        main.log.info( "Waiting " + str( linkSleep ) +
+                       " seconds for link up to be discovered" )
+        time.sleep( linkSleep )
+        utilities.assert_equals( expect=main.TRUE, actual=LinkUp,
+                                 onpass="Link up successful",
+                                 onfail="Failed to bring link up" )
+        # TODO do some sort of check here
+
+    def CASE11( self, main ):
+        """
+        Switch Down
+        """
+        # NOTE: You should probably run a topology check after this
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        switchSleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
+
+        description = "Killing a switch to ensure it is discovered correctly"
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        main.case( description )
+        switch = main.params[ 'kill' ][ 'switch' ]
+        switchDPID = main.params[ 'kill' ][ 'dpid' ]
+
+        # TODO: Make this switch parameterizable
+        main.step( "Kill " + switch )
+        main.log.info( "Deleting " + switch )
+        main.Mininet1.delSwitch( switch )
+        main.log.info( "Waiting " + str( switchSleep ) +
+                       " seconds for switch down to be discovered" )
+        time.sleep( switchSleep )
+        device = onosCli.getDevice( dpid=switchDPID )
+        # Peek at the deleted switch
+        main.log.warn( str( device ) )
+        result = main.FALSE
+        if device and device[ 'available' ] is False:
+            result = main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=result,
+                                 onpass="Kill switch successful",
+                                 onfail="Failed to kill switch?" )
+
+    def CASE12( self, main ):
+        """
+        Switch Up
+        """
+        # NOTE: You should probably run a topology check after this
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        switchSleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
+        switch = main.params[ 'kill' ][ 'switch' ]
+        switchDPID = main.params[ 'kill' ][ 'dpid' ]
+        links = main.params[ 'kill' ][ 'links' ].split()
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        description = "Adding a switch to ensure it is discovered correctly"
+        main.case( description )
+
+        main.step( "Add back " + switch )
+        main.Mininet1.addSwitch( switch, dpid=switchDPID )
+        for peer in links:
+            main.Mininet1.addLink( switch, peer )
+        ipList = [ node.ip_address for node in main.nodes ]
+        main.Mininet1.assignSwController( sw=switch, ip=ipList )
+        main.log.info( "Waiting " + str( switchSleep ) +
+                       " seconds for switch up to be discovered" )
+        time.sleep( switchSleep )
+        device = onosCli.getDevice( dpid=switchDPID )
+        # Peek at the deleted switch
+        main.log.warn( str( device ) )
+        result = main.FALSE
+        if device and device[ 'available' ]:
+            result = main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=result,
+                                 onpass="add switch successful",
+                                 onfail="Failed to add switch?" )
+
+    def CASE13( self, main ):
+        """
+        Clean up
+        """
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Test Cleanup" )
+        main.step( "Killing tcpdumps" )
+        main.Mininet2.stopTcpdump()
+
+        if main.params[ 'BACKUP' ][ 'ENABLED' ] == "True":
+            main.step( "Copying MN pcap and ONOS log files to test station" )
+            # NOTE: MN Pcap file is being saved to logdir.
+            #       We scp this file as MN and TestON aren't necessarily the same vm
+
+            # FIXME: To be replaced with a Jenkin's post script
+            # TODO: Load these from params
+            # NOTE: must end in /
+            logFolder = "/opt/onos/log/"
+            logFiles = [ "karaf.log", "karaf.log.1" ]
+            # NOTE: must end in /
+            for f in logFiles:
+                for node in main.nodes:
+                    dstName = main.logdir + "/" + node.name + "-" + f
+                    main.ONOSbench.secureCopy( node.user_name, node.ip_address,
+                                               logFolder + f, dstName )
+            # std*.log's
+            # NOTE: must end in /
+            logFolder = "/opt/onos/var/"
+            logFiles = [ "stderr.log", "stdout.log" ]
+            # NOTE: must end in /
+            for f in logFiles:
+                for node in main.nodes:
+                    dstName = main.logdir + "/" + node.name + "-" + f
+                    main.ONOSbench.secureCopy( node.user_name, node.ip_address,
+                                               logFolder + f, dstName )
+        else:
+            main.log.debug( "skipping saving log files" )
+
+        main.step( "Stopping Mininet" )
+        mnResult = main.Mininet1.stopNet()
+        utilities.assert_equals( expect=main.TRUE, actual=mnResult,
+                                 onpass="Mininet stopped",
+                                 onfail="MN cleanup NOT successful" )
+
+        main.step( "Checking ONOS Logs for errors" )
+        for node in main.nodes:
+            main.log.debug( "Checking logs for errors on " + node.name + ":" )
+            main.log.warn( main.ONOSbench.checkLogs( node.ip_address ) )
+
+        try:
+            timerLog = open( main.logdir + "/Timers.csv", 'w')
+            main.log.error( ", ".join( labels ) + "\n" + ", ".join( data ) )
+            timerLog.write( ", ".join( labels ) + "\n" + ", ".join( data ) )
+            timerLog.close()
+        except NameError, e:
+            main.log.exception(e)
+
+        main.step( "Stopping webserver" )
+        status = main.Server.stop( )
+        utilities.assert_equals( expect=main.TRUE, actual=status,
+                                 onpass="Stop Server",
+                                 onfail="Failled to stop SimpleHTTPServer" )
+        del main.Server
+
+    def CASE14( self, main ):
+        """
+        start election app on all onos nodes
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case("Start Leadership Election app")
+        main.step( "Install leadership election app" )
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        appResult = onosCli.activateApp( "org.onosproject.election" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=appResult,
+            onpass="Election app installed",
+            onfail="Something went wrong with installing Leadership election" )
+
+        main.step( "Run for election on each node" )
+        for i in main.activeNodes:
+            main.CLIs[i].electionTestRun()
+        time.sleep(5)
+        activeCLIs = [ main.CLIs[i] for i in main.activeNodes ]
+        sameResult, leaders = main.HA.consistentLeaderboards( activeCLIs )
+        utilities.assert_equals(
+            expect=True,
+            actual=sameResult,
+            onpass="All nodes see the same leaderboards",
+            onfail="Inconsistent leaderboards" )
+
+        if sameResult:
+            leader = leaders[ 0 ][ 0 ]
+            if main.nodes[ main.activeNodes[0] ].ip_address in leader:
+                correctLeader = True
+            else:
+                correctLeader = False
+            main.step( "First node was elected leader" )
+            utilities.assert_equals(
+                expect=True,
+                actual=correctLeader,
+                onpass="Correct leader was elected",
+                onfail="Incorrect leader" )
+
+    def CASE15( self, main ):
+        """
+        Check that Leadership Election is still functional
+            15.1 Run election on each node
+            15.2 Check that each node has the same leaders and candidates
+            15.3 Find current leader and withdraw
+            15.4 Check that a new node was elected leader
+            15.5 Check that that new leader was the candidate of old leader
+            15.6 Run for election on old leader
+            15.7 Check that oldLeader is a candidate, and leader if only 1 node
+            15.8 Make sure that the old leader was added to the candidate list
+
+            old and new variable prefixes refer to data from before vs after
+                withdrawl and later before withdrawl vs after re-election
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        description = "Check that Leadership Election is still functional"
+        main.case( description )
+        # NOTE: Need to re-run after restarts since being a canidate is not persistant
+
+        oldLeaders = []  # list of lists of each nodes' candidates before
+        newLeaders = []  # list of lists of each nodes' candidates after
+        oldLeader = ''  # the old leader from oldLeaders, None if not same
+        newLeader = ''  # the new leaders fron newLoeaders, None if not same
+        oldLeaderCLI = None  # the CLI of the old leader used for re-electing
+        expectNoLeader = False  # True when there is only one leader
+        if main.numCtrls == 1:
+            expectNoLeader = True
+
+        main.step( "Run for election on each node" )
+        electionResult = main.TRUE
+
+        for i in main.activeNodes:  # run test election on each node
+            if main.CLIs[i].electionTestRun() == main.FALSE:
+                electionResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=electionResult,
+            onpass="All nodes successfully ran for leadership",
+            onfail="At least one node failed to run for leadership" )
+
+        if electionResult == main.FALSE:
+            main.log.error(
+                "Skipping Test Case because Election Test App isn't loaded" )
+            main.skipCase()
+
+        main.step( "Check that each node shows the same leader and candidates" )
+        failMessage = "Nodes have different leaderboards"
+        activeCLIs = [ main.CLIs[i] for i in main.activeNodes ]
+        sameResult, oldLeaders = main.HA.consistentLeaderboards( activeCLIs )
+        if sameResult:
+            oldLeader = oldLeaders[ 0 ][ 0 ]
+            main.log.warn( oldLeader )
+        else:
+            oldLeader = None
+        utilities.assert_equals(
+            expect=True,
+            actual=sameResult,
+            onpass="Leaderboards are consistent for the election topic",
+            onfail=failMessage )
+
+        main.step( "Find current leader and withdraw" )
+        withdrawResult = main.TRUE
+        # do some sanity checking on leader before using it
+        if oldLeader is None:
+            main.log.error( "Leadership isn't consistent." )
+            withdrawResult = main.FALSE
+        # Get the CLI of the oldLeader
+        for i in main.activeNodes:
+            if oldLeader == main.nodes[ i ].ip_address:
+                oldLeaderCLI = main.CLIs[ i ]
+                break
+        else:  # FOR/ELSE statement
+            main.log.error( "Leader election, could not find current leader" )
+        if oldLeader:
+            withdrawResult = oldLeaderCLI.electionTestWithdraw()
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=withdrawResult,
+            onpass="Node was withdrawn from election",
+            onfail="Node was not withdrawn from election" )
+
+        main.step( "Check that a new node was elected leader" )
+        failMessage = "Nodes have different leaders"
+        # Get new leaders and candidates
+        newLeaderResult, newLeaders = main.HA.consistentLeaderboards( activeCLIs )
+        newLeader = None
+        if newLeaderResult:
+            if newLeaders[ 0 ][ 0 ] == 'none':
+                main.log.error( "No leader was elected on at least 1 node" )
+                if not expectNoLeader:
+                    newLeaderResult = False
+            newLeader = newLeaders[ 0 ][ 0 ]
+
+        # Check that the new leader is not the older leader, which was withdrawn
+        if newLeader == oldLeader:
+            newLeaderResult = False
+            main.log.error( "All nodes still see old leader: " + str( oldLeader ) +
+                    " as the current leader" )
+        utilities.assert_equals(
+            expect=True,
+            actual=newLeaderResult,
+            onpass="Leadership election passed",
+            onfail="Something went wrong with Leadership election" )
+
+        main.step( "Check that that new leader was the candidate of old leader" )
+        # candidates[ 2 ] should become the top candidate after withdrawl
+        correctCandidateResult = main.TRUE
+        if expectNoLeader:
+            if newLeader == 'none':
+                main.log.info( "No leader expected. None found. Pass" )
+                correctCandidateResult = main.TRUE
+            else:
+                main.log.info( "Expected no leader, got: " + str( newLeader ) )
+                correctCandidateResult = main.FALSE
+        elif len( oldLeaders[0] ) >= 3:
+            if newLeader == oldLeaders[ 0 ][ 2 ]:
+                # correct leader was elected
+                correctCandidateResult = main.TRUE
+            else:
+                correctCandidateResult = main.FALSE
+                main.log.error( "Candidate {} was elected. {} should have had priority.".format(
+                                    newLeader, oldLeaders[ 0 ][ 2 ] ) )
+        else:
+            main.log.warn( "Could not determine who should be the correct leader" )
+            main.log.debug( oldLeaders[ 0 ] )
+            correctCandidateResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=correctCandidateResult,
+            onpass="Correct Candidate Elected",
+            onfail="Incorrect Candidate Elected" )
+
+        main.step( "Run for election on old leader( just so everyone " +
+                   "is in the hat )" )
+        if oldLeaderCLI is not None:
+            runResult = oldLeaderCLI.electionTestRun()
+        else:
+            main.log.error( "No old leader to re-elect" )
+            runResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=runResult,
+            onpass="App re-ran for election",
+            onfail="App failed to run for election" )
+
+        main.step(
+            "Check that oldLeader is a candidate, and leader if only 1 node" )
+        # verify leader didn't just change
+        # Get new leaders and candidates
+        reRunLeaders = []
+        time.sleep( 5 )  # Paremterize
+        positionResult, reRunLeaders = main.HA.consistentLeaderboards( activeCLIs )
+
+        # Check that the re-elected node is last on the candidate List
+        if not reRunLeaders[0]:
+            positionResult = main.FALSE
+        elif oldLeader != reRunLeaders[ 0 ][ -1 ]:
+            main.log.error( "Old Leader ({}) not in the proper position: {} ".format( str( oldLeader),
+                                                                                      str( reRunLeaders[ 0 ] ) ) )
+            positionResult = main.FALSE
+        utilities.assert_equals(
+            expect=True,
+            actual=positionResult,
+            onpass="Old leader successfully re-ran for election",
+            onfail="Something went wrong with Leadership election after " +
+                   "the old leader re-ran for election" )
+
+    def CASE16( self, main ):
+        """
+        Install Distributed Primitives app
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        # Variables for the distributed primitives tests
+        global pCounterName
+        global pCounterValue
+        global onosSet
+        global onosSetName
+        pCounterName = "TestON-Partitions"
+        pCounterValue = 0
+        onosSet = set([])
+        onosSetName = "TestON-set"
+
+        description = "Install Primitives app"
+        main.case( description )
+        main.step( "Install Primitives app" )
+        appName = "org.onosproject.distributedprimitives"
+        node = main.activeNodes[0]
+        appResults = main.CLIs[node].activateApp( appName )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=appResults,
+                                 onpass="Primitives app activated",
+                                 onfail="Primitives app not activated" )
+        time.sleep( 5 )  # To allow all nodes to activate
+
+    def CASE17( self, main ):
+        """
+        Check for basic functionality with distributed primitives
+        """
+        # Make sure variables are defined/set
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        assert pCounterName, "pCounterName not defined"
+        assert onosSetName, "onosSetName not defined"
+        # NOTE: assert fails if value is 0/None/Empty/False
+        try:
+            pCounterValue
+        except NameError:
+            main.log.error( "pCounterValue not defined, setting to 0" )
+            pCounterValue = 0
+        try:
+            onosSet
+        except NameError:
+            main.log.error( "onosSet not defined, setting to empty Set" )
+            onosSet = set([])
+        # Variables for the distributed primitives tests. These are local only
+        addValue = "a"
+        addAllValue = "a b c d e f"
+        retainValue = "c d e f"
+
+        description = "Check for basic functionality with distributed " +\
+                      "primitives"
+        main.case( description )
+        main.caseExplanation = "Test the methods of the distributed " +\
+                                "primitives (counters and sets) throught the cli"
+        # DISTRIBUTED ATOMIC COUNTERS
+        # Partitioned counters
+        main.step( "Increment then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterAddAndGet-" + str( i ),
+                             args=[ pCounterName ] )
+            pCounterValue += 1
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Get then Increment a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestGetAndAdd,
+                             name="counterGetAndAdd-" + str( i ),
+                             args=[ pCounterName ] )
+            addedPValues.append( pCounterValue )
+            pCounterValue += 1
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Counters we added have the correct values" )
+        incrementCheck = main.HA.counterCheck( pCounterName, pCounterValue )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=incrementCheck,
+                                 onpass="Added counters are correct",
+                                 onfail="Added counters are incorrect" )
+
+        main.step( "Add -8 to then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": -8 } )
+            pCounterValue += -8
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Add 5 to then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": 5 } )
+            pCounterValue += 5
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Get then add 5 to a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestGetAndAdd,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": 5 } )
+            addedPValues.append( pCounterValue )
+            pCounterValue += 5
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Counters we added have the correct values" )
+        incrementCheck = main.HA.counterCheck( pCounterName, pCounterValue )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=incrementCheck,
+                                 onpass="Added counters are correct",
+                                 onfail="Added counters are incorrect" )
+
+        # DISTRIBUTED SETS
+        main.step( "Distributed Set get" )
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=getResults,
+                                 onpass="Set elements are correct",
+                                 onfail="Set elements are incorrect" )
+
+        main.step( "Distributed Set size" )
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=sizeResults,
+                                 onpass="Set sizes are correct",
+                                 onfail="Set sizes are incorrect" )
+
+        main.step( "Distributed Set add()" )
+        onosSet.add( addValue )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAdd-" + str( i ),
+                             args=[ onosSetName, addValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addResults = main.FALSE
+            else:
+                # unexpected result
+                addResults = main.FALSE
+        if addResults != main.TRUE:
+            main.log.error( "Error executing set add" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node + " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node + " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addResults = addResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addResults,
+                                 onpass="Set add correct",
+                                 onfail="Set add was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set contains()" )
+        containsResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setContains-" + str( i ),
+                             args=[ onosSetName ],
+                             kwargs={ "values": addValue } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            # NOTE: This is the tuple
+            containsResponses.append( t.result )
+
+        containsResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if containsResponses[ i ] == main.ERROR:
+                containsResults = main.FALSE
+            else:
+                containsResults = containsResults and\
+                                  containsResponses[ i ][ 1 ]
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=containsResults,
+                                 onpass="Set contains is functional",
+                                 onfail="Set contains failed" )
+
+        main.step( "Distributed Set containsAll()" )
+        containsAllResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setContainsAll-" + str( i ),
+                             args=[ onosSetName ],
+                             kwargs={ "values": addAllValue } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            # NOTE: This is the tuple
+            containsAllResponses.append( t.result )
+
+        containsAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if containsResponses[ i ] == main.ERROR:
+                containsResults = main.FALSE
+            else:
+                containsResults = containsResults and\
+                                  containsResponses[ i ][ 1 ]
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=containsAllResults,
+                                 onpass="Set containsAll is functional",
+                                 onfail="Set containsAll failed" )
+
+        main.step( "Distributed Set remove()" )
+        onosSet.remove( addValue )
+        removeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestRemove-" + str( i ),
+                             args=[ onosSetName, addValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            removeResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        removeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if removeResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif removeResponses[ i ] == main.FALSE:
+                # not in set, probably fine
+                pass
+            elif removeResponses[ i ] == main.ERROR:
+                # Error in execution
+                removeResults = main.FALSE
+            else:
+                # unexpected result
+                removeResults = main.FALSE
+        if removeResults != main.TRUE:
+            main.log.error( "Error executing set remove" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        removeResults = removeResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeResults,
+                                 onpass="Set remove correct",
+                                 onfail="Set remove was incorrect" )
+
+        main.step( "Distributed Set removeAll()" )
+        onosSet.difference_update( addAllValue.split() )
+        removeAllResponses = []
+        threads = []
+        try:
+            for i in main.activeNodes:
+                t = main.Thread( target=main.CLIs[i].setTestRemove,
+                                 name="setTestRemoveAll-" + str( i ),
+                                 args=[ onosSetName, addAllValue ] )
+                threads.append( t )
+                t.start()
+            for t in threads:
+                t.join()
+                removeAllResponses.append( t.result )
+        except Exception, e:
+            main.log.exception(e)
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        removeAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if removeAllResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif removeAllResponses[ i ] == main.FALSE:
+                # not in set, probably fine
+                pass
+            elif removeAllResponses[ i ] == main.ERROR:
+                # Error in execution
+                removeAllResults = main.FALSE
+            else:
+                # unexpected result
+                removeAllResults = main.FALSE
+        if removeAllResults != main.TRUE:
+            main.log.error( "Error executing set removeAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        removeAllResults = removeAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeAllResults,
+                                 onpass="Set removeAll correct",
+                                 onfail="Set removeAll was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set clear()" )
+        onosSet.clear()
+        clearResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestClear-" + str( i ),
+                             args=[ onosSetName, " "],  # Values doesn't matter
+                             kwargs={ "clear": True } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            clearResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        clearResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if clearResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif clearResponses[ i ] == main.FALSE:
+                # Nothing set, probably fine
+                pass
+            elif clearResponses[ i ] == main.ERROR:
+                # Error in execution
+                clearResults = main.FALSE
+            else:
+                # unexpected result
+                clearResults = main.FALSE
+        if clearResults != main.TRUE:
+            main.log.error( "Error executing set clear" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        clearResults = clearResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=clearResults,
+                                 onpass="Set clear correct",
+                                 onfail="Set clear was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set retain()" )
+        onosSet.intersection_update( retainValue.split() )
+        retainResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestRetain-" + str( i ),
+                             args=[ onosSetName, retainValue ],
+                             kwargs={ "retain": True } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            retainResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        retainResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if retainResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif retainResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif retainResponses[ i ] == main.ERROR:
+                # Error in execution
+                retainResults = main.FALSE
+            else:
+                # unexpected result
+                retainResults = main.FALSE
+        if retainResults != main.TRUE:
+            main.log.error( "Error executing set retain" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node + " expected a size of " +
+                                str( size ) + " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        retainResults = retainResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=retainResults,
+                                 onpass="Set retain correct",
+                                 onfail="Set retain was incorrect" )
+
+        # Transactional maps
+        main.step( "Partitioned Transactional maps put" )
+        tMapValue = "Testing"
+        numKeys = 100
+        putResult = True
+        node = main.activeNodes[0]
+        putResponses = main.CLIs[node].transactionalMapPut( numKeys, tMapValue )
+        if putResponses and len( putResponses ) == 100:
+            for i in putResponses:
+                if putResponses[ i ][ 'value' ] != tMapValue:
+                    putResult = False
+        else:
+            putResult = False
+        if not putResult:
+            main.log.debug( "Put response values: " + str( putResponses ) )
+        utilities.assert_equals( expect=True,
+                                 actual=putResult,
+                                 onpass="Partitioned Transactional Map put successful",
+                                 onfail="Partitioned Transactional Map put values are incorrect" )
+
+        main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
+        getCheck = True
+        for n in range( 1, numKeys + 1 ):
+            getResponses = []
+            threads = []
+            valueCheck = True
+            for i in main.activeNodes:
+                t = main.Thread( target=main.CLIs[i].transactionalMapGet,
+                                 name="TMap-get-" + str( i ),
+                                 args=[ "Key" + str( n ) ] )
+                threads.append( t )
+                t.start()
+            for t in threads:
+                t.join()
+                getResponses.append( t.result )
+            for node in getResponses:
+                if node != tMapValue:
+                    valueCheck = False
+            if not valueCheck:
+                main.log.warn( "Values for key 'Key" + str( n ) + "' do not match:" )
+                main.log.warn( getResponses )
+            getCheck = getCheck and valueCheck
+        utilities.assert_equals( expect=True,
+                                 actual=getCheck,
+                                 onpass="Partitioned Transactional Map get values were correct",
+                                 onfail="Partitioned Transactional Map values incorrect" )
diff --git a/TestON/tests/HA/HAscaling/HAscaling.topo b/TestON/tests/HA/HAscaling/HAscaling.topo
new file mode 100644
index 0000000..81cf47a
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/HAscaling.topo
@@ -0,0 +1,171 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOScli1>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli1>
+
+        <ONOScli2>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli2>
+
+        <ONOScli3>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli3>
+
+
+        <ONOScli4>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli4>
+
+
+        <ONOScli5>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli5>
+
+
+        <ONOScli6>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>7</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli6>
+
+
+        <ONOScli7>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>8</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli7>
+
+        <ONOS1>
+            <host>OC1</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>9</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS1>
+
+        <ONOS2>
+            <host>OC2</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>10</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS2>
+
+        <ONOS3>
+            <host>OC3</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>11</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS3>
+
+        <ONOS4>
+            <host>OC4</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>12</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS4>
+
+        <ONOS5>
+            <host>OC5</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>13</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS5>
+
+        <ONOS6>
+            <host>OC6</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>14</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS6>
+
+        <ONOS7>
+            <host>OC7</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>15</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS7>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>16</connect_order>
+            <COMPONENTS>
+                #Specify the Option for mininet
+                <arg1> --custom ~/mininet/custom/obelisk.py </arg1>
+                <arg2> --topo obelisk </arg2>
+                <arg3> --switch ovs,protocols=OpenFlow13 </arg3>
+                <controller> none </controller>
+                <home>~/mininet/custom/</home>
+            </COMPONENTS>
+        </Mininet1>
+
+        <Mininet2>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>RemoteMininetDriver</type>
+            <connect_order>17</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </Mininet2>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/HA/HAscaling/README b/TestON/tests/HA/HAscaling/README
new file mode 100644
index 0000000..281b7c2
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/README
@@ -0,0 +1,20 @@
+This test is designed to verify that an ONOS cluster behaves correctly when
+nodes are added or removed from a cluster dynamically via modifying a remote
+cluster metadata file.
+
+
+The gerneral structure for the test:
+- Startup
+- Assign switches
+- Verify ONOS state and functionality
+    - Device mastership
+    - Intents
+    - Leadership election
+    - Distributed Primitives
+- Scale ONOS cluster size from 1 to 7 to 1 by increments of 2
+    - Modify cluster metadata file
+    - Start or stop ONOS nodes
+    - Verify ONOS state and functionality
+    - Dataplane failures
+        - link down and up
+        - switch down and up
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/HA/HAscaling/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/HA/HAscaling/__init__.py
diff --git a/TestON/tests/HA/HAscaling/dependencies/Server.py b/TestON/tests/HA/HAscaling/dependencies/Server.py
new file mode 100644
index 0000000..bff976f
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/dependencies/Server.py
@@ -0,0 +1,148 @@
+"Functions for using the SimpleHTTPServer python module"
+import re
+
+class Server():
+
+    def __init__( self ):
+        self.default = ''
+        self.PID = -1
+        self.component = None
+        self.rootDir = None
+
+    def __del__( self ):
+        self.stop()
+
+    def start( self, component, rootDir, port=8000, logDir=None ):
+        """
+        Start SimpleHTTPServer as a background process from rootDir on the
+        given component. The webserver will listen on port and if specified,
+        output will be redirected to logDir.
+
+        Arguments:
+        - component = The TestON component handle to start the webserver on
+        - rootDir = The root directory for the web content
+        - port = The port number for the webserver to listen on. Defaults to 8000
+        - logDir = If specified, the output of the webserver will be redirected
+                   to this file. Note that this should be either an absolute path
+                   or relative to rootDir.
+        Returns:
+            main.TRUE if the command succedes or main.FALSE if there is an error.
+        """
+        retValue = main.TRUE
+        self.rootDir = rootDir
+        try:
+            # Save component for this instance so other functions can use it
+            self.component = component
+            main.log.info( "Starting SimpleHTTPServer on " + component.name )
+            if component.handle:
+                handle = component.handle
+                # cd to rootDir
+                handle.sendline( "cd " + str( rootDir ) )
+                handle.expect( "\$" )
+                # Start server
+                cmd = "python -m SimpleHTTPServer {}".format( port )
+                if logDir:
+                    cmd += " &> {}".format( logDir )  # pipe all output to a file
+                else:
+                    cmd += "&> {dev/null}" # Throw away all output
+                cmd += " &"
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                response = handle.before
+                # Return to home dir
+                handle.sendline( "cd " + component.home )
+                handle.expect( "\$" )
+                response += handle.before
+                if "Exit" in response:
+                    main.log.error( "Error starting server. Check server log for details" )
+                    main.log.debug( handle.before )
+                    retValue = main.FALSE
+                # capture PID for later use
+                # EX: [1] 67987
+                match = re.search( "\[\d\] (?P<PID>\d+)", response )
+                if match:
+                    self.PID = match.group( "PID" )
+                else:
+                    main.log.warn( "Could not find PID" )
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error starting web server" )
+            retValue = main.FALSE
+        return retValue
+
+    def stop( self ):
+        """
+        Kills the process of the server. Note that this function must be run
+        from the same instance of the server class that the server was started
+        on.
+        """
+        retValue = main.TRUE
+        try:
+            main.log.info( "Stopping Server." )
+            assert self.component, "Component not specified"
+            assert self.PID, "PID not found"
+            if self.component.handle:
+                handle = self.component.handle
+                cmd = "sudo kill {}".format( self.PID )
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                # TODO: What is bad output? cannot sudo?
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error stopping web server" )
+            retValue = main.FALSE
+        return retValue
+
+    def generateFile( self, nodes, equal=False, filename="cluster.json" ):
+        """
+        Generate custom metadata file in the root directory using the custom
+        onos-gen-partitions file which should also be located in the root
+        directory.
+
+        Note that this function needs to be run after the start function has
+        been called for this instance.
+
+        Arguments:
+        - nodes = The number of ONOS nodes to include in the cluster. Will
+                  include nodes in ascending order, I.E. OC1, OC2, etc
+
+        Optional Arguments:
+        - equal = Specifies whether all nodes should participate in every
+                  partition. Defaults to False.
+        - filename = The name of the file to save the cluster metadata to.
+                     Defaults to "cluster.json".
+        Returns:
+            main.TRUE if the command succedes or main.FALSE if there is an error.
+        """
+        retValue = main.TRUE
+        try:
+            if self.component.handle:
+                assert self.component, "Component not specified. Please start the server first"
+                assert self.rootDir, "Root directory not found"
+                handle = self.component.handle
+                # cd to rootDir
+                handle.sendline( "cd " + str( self.rootDir ) )
+                handle.expect( "\$" )
+                cmd = "./onos-gen-partitions {} {} ".format( filename, nodes )
+                if equal:
+                    cmd += "-e"
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                response = handle.before
+                # Return to home dir
+                handle.sendline( "cd " + self.component.home )
+                handle.expect( "\$" )
+                response += handle.before
+                if "Traceback" in response:
+                    main.log.error( handle.before )
+                    retValue = main.FALSE
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error generating metadata file" )
+        return retValue
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/HA/HAscaling/dependencies/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/HA/HAscaling/dependencies/__init__.py
diff --git a/TestON/tests/HA/HAscaling/dependencies/onos-gen-partitions b/TestON/tests/HA/HAscaling/dependencies/onos-gen-partitions
new file mode 100755
index 0000000..2e49db0
--- /dev/null
+++ b/TestON/tests/HA/HAscaling/dependencies/onos-gen-partitions
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+"""
+  Generate the partitions json file from the $OC* environment variables
+
+  Usage: onos-gen-partitions output_file [num_nodes] [-e]
+  If output file is not provided, the json is written to stdout.
+"""
+
+from os import environ
+from collections import deque, OrderedDict
+import re
+import json
+import sys
+import hashlib
+
+convert = lambda text: int(text) if text.isdigit() else text.lower()
+alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
+
+def get_OC_vars():
+  vars = []
+  for var in environ:
+    if re.match(r"OC[0-9]+", var):
+      vars.append(var)
+  return sorted(vars, key=alphanum_key)
+
+def get_nodes(vars, port=9876):
+  node = lambda k: { 'id': k, 'ip': k, 'port': port }
+  return [ node(environ[v]) for v in vars ]
+
+def generate_base_partition(nodes):
+  return {
+            'id': 0,
+            'members': nodes
+         }
+
+def generate_extended_partitions_scaling(nodes, k, partitions=3, equal=False):
+  l = deque(nodes)
+  perms = []
+  for i in range(1, partitions + 1):
+    if equal:
+      members = list(l)
+    else:
+      members = list(l)[:k]
+
+    part = {
+             'id': i,
+             'members': members
+           }
+    perms.append(part)
+    l.rotate(-2)
+  return perms
+
+if __name__ == '__main__':
+  vars = get_OC_vars()
+  # NOTE: likely prone to errors
+  nodes = get_nodes(vars)
+  num = None
+  if len(sys.argv) >= 3:
+      num = int(sys.argv[2])
+      try:
+          equal = "-e" in sys.argv[3]
+      except:
+          equal = False
+  if num:
+      nodes = nodes[:num]
+
+  base_partition = generate_base_partition([v.get('id') for v in nodes])
+  extended_partitions = generate_extended_partitions_scaling([v.get('id') for v in nodes],
+          3, equal=equal)
+  partitions = []
+  partitions.append(base_partition)
+  partitions.extend(extended_partitions)
+  name = hash("HAScaling")
+  data = {
+           'name': name,
+           'nodes': nodes,
+           'partitions': partitions
+         }
+  output = json.dumps(data, indent=4)
+
+  if len(sys.argv) >= 2:
+    filename = sys.argv[1]
+    with open(filename, 'w') as f:
+      f.write(output)
+  else:
+    print output
diff --git a/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py b/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
index 25aa0ba..1d4db86 100644
--- a/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
+++ b/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
@@ -251,12 +251,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -3425,6 +3424,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAstopNodes/HAstopNodes.params b/TestON/tests/HA/HAstopNodes/HAstopNodes.params
index d2eb635..dd035f5 100644
--- a/TestON/tests/HA/HAstopNodes/HAstopNodes.params
+++ b/TestON/tests/HA/HAstopNodes/HAstopNodes.params
@@ -19,7 +19,7 @@
     #CASE15: Check that Leadership Election is still functional
     #CASE16: Install Distributed Primitives app
     #CASE17: Check for basic functionality with distributed primitives
-    <testcases>1,2,8,3,4,5,14,16,17,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+    <testcases>1,2,8,21,3,4,5,14,16,17,[61,8,7,4,15,17,62],8,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
 
     <apps></apps>
     <ONOS_Configuration>
diff --git a/TestON/tests/HA/HAstopNodes/HAstopNodes.py b/TestON/tests/HA/HAstopNodes/HAstopNodes.py
index 240c9c4..0f33ec0 100644
--- a/TestON/tests/HA/HAstopNodes/HAstopNodes.py
+++ b/TestON/tests/HA/HAstopNodes/HAstopNodes.py
@@ -286,12 +286,11 @@
                                  onfail="Nodes check NOT successful" )
 
         if not nodeResults:
-            for cli in main.CLIs:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
                 main.log.debug( "{} components not ACTIVE: \n{}".format(
                     cli.name,
                     cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
-
-        if cliResults == main.FALSE:
             main.log.error( "Failed to start ONOS, stopping test" )
             main.cleanup()
             main.exit()
@@ -1681,8 +1680,9 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
-        if consistentClustersResult != main.TRUE:
+        if not consistentClustersResult:
             main.log.debug( clusters )
+
         # there should always only be one cluster
         main.step( "Cluster view correct across ONOS nodes" )
         try:
@@ -2552,6 +2552,8 @@
             actual=consistentClustersResult,
             onpass="Clusters view is consistent across all ONOS nodes",
             onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
 
         main.step( "There is only one SCC" )
         # there should always only be one cluster
@@ -4211,6 +4213,9 @@
                                  onfail="Partitioned Transactional Map put values are incorrect" )
 
         main.step( "Partitioned Transactional maps get" )
+        # FIXME: is this sleep needed?
+        time.sleep( 5 )
+
         getCheck = True
         for n in range( 1, numKeys + 1 ):
             getResponses = []
diff --git a/TestON/tests/HA/HAswapNodes/HAswapNodes.params b/TestON/tests/HA/HAswapNodes/HAswapNodes.params
new file mode 100644
index 0000000..d3729a4
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/HAswapNodes.params
@@ -0,0 +1,92 @@
+<PARAMS>
+    #CASE1: Compile ONOS and push it to the test machines
+    #CASE2: Assign devices to controllers
+    #CASE21: Assign mastership to controllers
+    #CASE3: Assign intents
+    #CASE4: Ping across added host intents
+    #CASE5: Reading state of ONOS
+    #CASE6: Swap nodes
+    #CASE7: Check state after control plane failure
+    #CASE8: Compare topo
+    #CASE9: Link s3-s28 down
+    #CASE10: Link s3-s28 up
+    #CASE11: Switch down
+    #CASE12: Switch up
+    #CASE13: Clean up
+    #CASE14: start election app on all onos nodes
+    #CASE15: Check that Leadership Election is still functional
+    #CASE16: Install Distributed Primitives app
+    #CASE17: Check for basic functionality with distributed primitives
+    <testcases>1,[2,8,21,3,8,4,5,14,16,17]*1,6,8,3,7,4,15,17,9,8,4,10,8,4,11,8,4,12,8,4,13</testcases>
+
+    <server>
+        <port>8000</port>
+        <interface>eth0</interface>
+    </server>
+    <apps></apps>
+    <ONOS_Configuration>
+        <org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+            <useFlowObjectives>true</useFlowObjectives>
+        </org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+    </ONOS_Configuration>
+    <ENV>
+        <cellName>HA</cellName>
+        <appString>drivers,openflow,proxyarp,mobility</appString>
+    </ENV>
+    <Git> False </Git>
+    <branch> master </branch>
+    <num_controllers> 7 </num_controllers>
+    <tcpdump> False </tcpdump>
+
+    <CTRL>
+        <port1>6653</port1>
+        <port2>6653</port2>
+        <port3>6653</port3>
+        <port4>6653</port4>
+        <port5>6653</port5>
+        <port6>6653</port6>
+        <port7>6653</port7>
+    </CTRL>
+    <BACKUP>
+        <ENABLED> False </ENABLED>
+        <TESTONUSER>sdn</TESTONUSER>
+        <TESTONIP>10.128.30.9</TESTONIP>
+    </BACKUP>
+    <PING>
+        <source1>h8</source1>
+        <source2>h9</source2>
+        <source3>h10</source3>
+        <source4>h11</source4>
+        <source5>h12</source5>
+        <source6>h13</source6>
+        <source7>h14</source7>
+        <source8>h15</source8>
+        <source9>h16</source9>
+        <source10>h17</source10>
+        <target1>10.0.0.18</target1>
+        <target2>10.0.0.19</target2>
+        <target3>10.0.0.20</target3>
+        <target4>10.0.0.21</target4>
+        <target5>10.0.0.22</target5>
+        <target6>10.0.0.23</target6>
+        <target7>10.0.0.24</target7>
+        <target8>10.0.0.25</target8>
+        <target9>10.0.0.26</target9>
+        <target10>10.0.0.27</target10>
+    </PING>
+    <timers>
+        <LinkDiscovery>12</LinkDiscovery>
+        <SwitchDiscovery>12</SwitchDiscovery>
+        <gossip>5</gossip>
+    </timers>
+    <kill>
+        <switch> s5 </switch>
+        <dpid> 0000000000005000 </dpid>
+        <links> h5 s2 s1 s6 </links>
+    </kill>
+    <MNtcpdump>
+        <intf>eth0</intf>
+        <port> </port>
+        <folder>~/packet_captures/</folder>
+    </MNtcpdump>
+</PARAMS>
diff --git a/TestON/tests/HA/HAswapNodes/HAswapNodes.py b/TestON/tests/HA/HAswapNodes/HAswapNodes.py
new file mode 100644
index 0000000..8ea1490
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/HAswapNodes.py
@@ -0,0 +1,4289 @@
+"""
+Description: This test is to determine if ONOS can handle
+             dynamic swapping of cluster nodes.
+
+List of test cases:
+CASE1: Compile ONOS and push it to the test machines
+CASE2: Assign devices to controllers
+CASE21: Assign mastership to controllers
+CASE3: Assign intents
+CASE4: Ping across added host intents
+CASE5: Reading state of ONOS
+CASE6: Swap nodes
+CASE7: Check state after control plane failure
+CASE8: Compare topo
+CASE9: Link s3-s28 down
+CASE10: Link s3-s28 up
+CASE11: Switch down
+CASE12: Switch up
+CASE13: Clean up
+CASE14: start election app on all onos nodes
+CASE15: Check that Leadership Election is still functional
+CASE16: Install Distributed Primitives app
+CASE17: Check for basic functionality with distributed primitives
+"""
+
+
+class HAswapNodes:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE1( self, main ):
+        """
+        CASE1 is to compile ONOS and push it to the test machines
+
+        Startup sequence:
+        cell <name>
+        onos-verify-cell
+        NOTE: temporary - onos-remove-raft-logs
+        onos-uninstall
+        start mininet
+        git pull
+        mvn clean install
+        onos-package
+        onos-install -f
+        onos-wait-for-start
+        start cli sessions
+        start tcpdump
+        """
+        import time
+        import os
+        import re
+        main.log.info( "ONOS HA test: Restart all ONOS nodes - " +
+                         "initialization" )
+        main.case( "Setting up test environment" )
+        main.caseExplanation = "Setup the test environment including " +\
+                                "installing ONOS, starting Mininet and ONOS" +\
+                                "cli sessions."
+
+        # load some variables from the params file
+        PULLCODE = False
+        if main.params[ 'Git' ] == 'True':
+            PULLCODE = True
+        gitBranch = main.params[ 'branch' ]
+        cellName = main.params[ 'ENV' ][ 'cellName' ]
+
+        main.numCtrls = int( main.params[ 'num_controllers' ] )
+        if main.ONOSbench.maxNodes:
+            if main.ONOSbench.maxNodes < main.numCtrls:
+                main.numCtrls = int( main.ONOSbench.maxNodes )
+        # set global variables
+        # These are for csv plotting in jenkins
+        global labels
+        global data
+        labels = []
+        data = []
+
+        try:
+            from tests.HA.dependencies.HA import HA
+            main.HA = HA()
+            from tests.HA.HAswapNodes.dependencies.Server import Server
+            main.Server = Server()
+        except Exception as e:
+            main.log.exception( e )
+            main.cleanup()
+            main.exit()
+
+        main.CLIs = []
+        main.nodes = []
+        ipList = []
+        for i in range( 1, main.numCtrls + 1 ):
+            try:
+                main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
+                main.nodes.append( getattr( main, 'ONOS' + str( i ) ) )
+                ipList.append( main.nodes[ -1 ].ip_address )
+            except AttributeError:
+                break
+
+        main.step( "Create cell file" )
+        cellAppString = main.params[ 'ENV' ][ 'appString' ]
+        main.ONOSbench.createCellFile( main.ONOSbench.ip_address, cellName,
+                                       main.Mininet1.ip_address,
+                                       cellAppString, ipList )
+
+        main.step( "Applying cell variable to environment" )
+        cellResult = main.ONOSbench.setCell( cellName )
+        utilities.assert_equals( expect=main.TRUE, actual=cellResult,
+                                 onpass="Set cell successfull",
+                                 onfail="Failled to set cell" )
+
+        main.step( "Verify connectivity to cell" )
+        verifyResult = main.ONOSbench.verifyCell()
+        utilities.assert_equals( expect=main.TRUE, actual=verifyResult,
+                                 onpass="Verify cell passed",
+                                 onfail="Failled to verify cell" )
+
+        # FIXME:this is short term fix
+        main.log.info( "Removing raft logs" )
+        main.ONOSbench.onosRemoveRaftLogs()
+
+        main.log.info( "Uninstalling ONOS" )
+        for node in main.nodes:
+            main.ONOSbench.onosUninstall( node.ip_address )
+
+        # Make sure ONOS is DEAD
+        main.log.info( "Killing any ONOS processes" )
+        killResults = main.TRUE
+        for node in main.nodes:
+            killed = main.ONOSbench.onosKill( node.ip_address )
+            killResults = killResults and killed
+
+        main.step( "Setup server for cluster metadata file" )
+        port = main.params['server']['port']
+        rootDir = os.path.dirname( main.testFile ) + "/dependencies"
+        main.log.debug( "Root dir: {}".format( rootDir ) )
+        status = main.Server.start( main.ONOSbench,
+                                    rootDir,
+                                    port=port,
+                                    logDir=main.logdir + "/server.log" )
+        utilities.assert_equals( expect=main.TRUE, actual=status,
+                                 onpass="Server started",
+                                 onfail="Failled to start SimpleHTTPServer" )
+
+        main.step( "Generate initial metadata file" )
+        if main.numCtrls >= 5:
+            main.numCtrls -= 2
+        else:
+            main.log.error( "Not enough ONOS nodes to run this test. Requires 5 or more" )
+        genResult = main.Server.generateFile( main.numCtrls )
+        utilities.assert_equals( expect=main.TRUE, actual=genResult,
+                                 onpass="New cluster metadata file generated",
+                                 onfail="Failled to generate new metadata file" )
+
+        cleanInstallResult = main.TRUE
+        gitPullResult = main.TRUE
+
+        main.step( "Starting Mininet" )
+        # scp topo file to mininet
+        # TODO: move to params?
+        topoName = "obelisk.py"
+        filePath = main.ONOSbench.home + "/tools/test/topos/"
+        main.ONOSbench.scp( main.Mininet1,
+                            filePath + topoName,
+                            main.Mininet1.home,
+                            direction="to" )
+        mnResult = main.Mininet1.startNet( )
+        utilities.assert_equals( expect=main.TRUE, actual=mnResult,
+                                 onpass="Mininet Started",
+                                 onfail="Error starting Mininet" )
+
+        main.step( "Git checkout and pull " + gitBranch )
+        if PULLCODE:
+            main.ONOSbench.gitCheckout( gitBranch )
+            gitPullResult = main.ONOSbench.gitPull()
+            # values of 1 or 3 are good
+            utilities.assert_lesser( expect=0, actual=gitPullResult,
+                                      onpass="Git pull successful",
+                                      onfail="Git pull failed" )
+        main.ONOSbench.getVersion( report=True )
+
+        main.step( "Using mvn clean install" )
+        cleanInstallResult = main.TRUE
+        if PULLCODE and gitPullResult == main.TRUE:
+            cleanInstallResult = main.ONOSbench.cleanInstall()
+        else:
+            main.log.warn( "Did not pull new code so skipping mvn " +
+                           "clean install" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cleanInstallResult,
+                                 onpass="MCI successful",
+                                 onfail="MCI failed" )
+        # GRAPHS
+        # NOTE: important params here:
+        #       job = name of Jenkins job
+        #       Plot Name = Plot-HA, only can be used if multiple plots
+        #       index = The number of the graph under plot name
+        job = "HAswapNodes"
+        plotName = "Plot-HA"
+        index = "0"
+        graphs = '<ac:structured-macro ac:name="html">\n'
+        graphs += '<ac:plain-text-body><![CDATA[\n'
+        graphs += '<iframe src="https://onos-jenkins.onlab.us/job/' + job +\
+                  '/plot/' + plotName + '/getPlot?index=' + index +\
+                  '&width=500&height=300"' +\
+                  'noborder="0" width="500" height="300" scrolling="yes" ' +\
+                  'seamless="seamless"></iframe>\n'
+        graphs += ']]></ac:plain-text-body>\n'
+        graphs += '</ac:structured-macro>\n'
+        main.log.wiki(graphs)
+
+        main.step( "Copying backup config files" )
+        path = "~/onos/tools/package/bin/onos-service"
+        cp = main.ONOSbench.scp( main.ONOSbench,
+                                     path,
+                                     path + ".backup",
+                                     direction="to" )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cp,
+                                 onpass="Copy backup config file succeeded",
+                                 onfail="Copy backup config file failed" )
+        # we need to modify the onos-service file to use remote metadata file
+        # url for cluster metadata file
+        iface = main.params['server'].get( 'interface' )
+        ip = main.ONOSbench.getIpAddr( iface=iface )
+        metaFile = "cluster.json"
+        javaArgs = r"-Donos.cluster.metadata.uri=http:\/\/{}:{}\/{}".format( ip, port, metaFile )
+        main.log.warn( javaArgs )
+        main.log.warn( repr( javaArgs ) )
+        handle = main.ONOSbench.handle
+        sed = r"sed -i 's/bash/bash\nexport JAVA_OPTS=${{JAVA_OPTS:-{}}}\n/' {}".format( javaArgs, path )
+        main.log.warn( sed )
+        main.log.warn( repr( sed ) )
+        handle.sendline( sed )
+        handle.expect( "\$" )
+        main.log.debug( repr( handle.before ) )
+
+        main.step( "Creating ONOS package" )
+        packageResult = main.ONOSbench.onosPackage()
+        utilities.assert_equals( expect=main.TRUE, actual=packageResult,
+                                 onpass="ONOS package successful",
+                                 onfail="ONOS package failed" )
+
+        main.step( "Installing ONOS package" )
+        onosInstallResult = main.TRUE
+        for i in range( main.ONOSbench.maxNodes ):
+            node = main.nodes[i]
+            options = "-f"
+            if i >= main.numCtrls:
+                options = "-nf"  # Don't start more than the current scale
+            tmpResult = main.ONOSbench.onosInstall( options=options,
+                                                    node=node.ip_address )
+            onosInstallResult = onosInstallResult and tmpResult
+        utilities.assert_equals( expect=main.TRUE, actual=onosInstallResult,
+                                 onpass="ONOS install successful",
+                                 onfail="ONOS install failed" )
+
+        # Cleanup custom onos-service file
+        main.ONOSbench.scp( main.ONOSbench,
+                            path + ".backup",
+                            path,
+                            direction="to" )
+
+        main.step( "Checking if ONOS is up yet" )
+        for i in range( 2 ):
+            onosIsupResult = main.TRUE
+            for i in range( main.numCtrls ):
+                node = main.nodes[i]
+                started = main.ONOSbench.isup( node.ip_address )
+                if not started:
+                    main.log.error( node.name + " hasn't started" )
+                onosIsupResult = onosIsupResult and started
+            if onosIsupResult == main.TRUE:
+                break
+        utilities.assert_equals( expect=main.TRUE, actual=onosIsupResult,
+                                 onpass="ONOS startup successful",
+                                 onfail="ONOS startup failed" )
+
+        main.log.step( "Starting ONOS CLI sessions" )
+        cliResults = main.TRUE
+        threads = []
+        for i in range( main.numCtrls ):
+            t = main.Thread( target=main.CLIs[i].startOnosCli,
+                             name="startOnosCli-" + str( i ),
+                             args=[main.nodes[i].ip_address] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            cliResults = cliResults and t.result
+        utilities.assert_equals( expect=main.TRUE, actual=cliResults,
+                                 onpass="ONOS cli startup successful",
+                                 onfail="ONOS cli startup failed" )
+
+        # Create a list of active nodes for use when some nodes are stopped
+        main.activeNodes = [ i for i in range( 0, main.numCtrls ) ]
+
+        if main.params[ 'tcpdump' ].lower() == "true":
+            main.step( "Start Packet Capture MN" )
+            main.Mininet2.startTcpdump(
+                str( main.params[ 'MNtcpdump' ][ 'folder' ] ) + str( main.TEST )
+                + "-MN.pcap",
+                intf=main.params[ 'MNtcpdump' ][ 'intf' ],
+                port=main.params[ 'MNtcpdump' ][ 'port' ] )
+
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+
+        if not nodeResults:
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
+                main.log.debug( "{} components not ACTIVE: \n{}".format(
+                    cli.name,
+                    cli.sendline( "scr:list | grep -v ACTIVE" ) ) )
+            main.log.error( "Failed to start ONOS, stopping test" )
+            main.cleanup()
+            main.exit()
+
+        main.step( "Activate apps defined in the params file" )
+        # get data from the params
+        apps = main.params.get( 'apps' )
+        if apps:
+            apps = apps.split(',')
+            main.log.warn( apps )
+            activateResult = True
+            for app in apps:
+                main.CLIs[ 0 ].app( app, "Activate" )
+            # TODO: check this worked
+            time.sleep( 10 )  # wait for apps to activate
+            for app in apps:
+                state = main.CLIs[ 0 ].appStatus( app )
+                if state == "ACTIVE":
+                    activateResult = activateResult and True
+                else:
+                    main.log.error( "{} is in {} state".format( app, state ) )
+                    activateResult = False
+            utilities.assert_equals( expect=True,
+                                     actual=activateResult,
+                                     onpass="Successfully activated apps",
+                                     onfail="Failed to activate apps" )
+        else:
+            main.log.warn( "No apps were specified to be loaded after startup" )
+
+        main.step( "Set ONOS configurations" )
+        config = main.params.get( 'ONOS_Configuration' )
+        if config:
+            main.log.debug( config )
+            checkResult = main.TRUE
+            for component in config:
+                for setting in config[component]:
+                    value = config[component][setting]
+                    check = main.CLIs[ 0 ].setCfg( component, setting, value )
+                    main.log.info( "Value was changed? {}".format( main.TRUE == check ) )
+                    checkResult = check and checkResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=checkResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+
+        main.step( "App Ids check" )
+        appCheck = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck = appCheck and t.result
+        if appCheck != main.TRUE:
+            node = main.activeNodes[0]
+            main.log.warn( main.CLIs[node].apps() )
+            main.log.warn( main.CLIs[node].appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+    def CASE2( self, main ):
+        """
+        Assign devices to controllers
+        """
+        import re
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Assigning devices to controllers" )
+        main.caseExplanation = "Assign switches to ONOS using 'ovs-vsctl' " +\
+                                "and check that an ONOS node becomes the " +\
+                                "master of the device."
+        main.step( "Assign switches to controllers" )
+
+        ipList = []
+        for i in range( main.ONOSbench.maxNodes ):
+            ipList.append( main.nodes[ i ].ip_address )
+        swList = []
+        for i in range( 1, 29 ):
+            swList.append( "s" + str( i ) )
+        main.Mininet1.assignSwController( sw=swList, ip=ipList )
+
+        mastershipCheck = main.TRUE
+        for i in range( 1, 29 ):
+            response = main.Mininet1.getSwController( "s" + str( i ) )
+            try:
+                main.log.info( str( response ) )
+            except Exception:
+                main.log.info( repr( response ) )
+            for node in main.nodes:
+                if re.search( "tcp:" + node.ip_address, response ):
+                    mastershipCheck = mastershipCheck and main.TRUE
+                else:
+                    main.log.error( "Error, node " + node.ip_address + " is " +
+                                    "not in the list of controllers s" +
+                                    str( i ) + " is connecting to." )
+                    mastershipCheck = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=mastershipCheck,
+            onpass="Switch mastership assigned correctly",
+            onfail="Switches not assigned correctly to controllers" )
+
+    def CASE21( self, main ):
+        """
+        Assign mastership to controllers
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Assigning Controller roles for switches" )
+        main.caseExplanation = "Check that ONOS is connected to each " +\
+                                "device. Then manually assign" +\
+                                " mastership to specific ONOS nodes using" +\
+                                " 'device-role'"
+        main.step( "Assign mastership of switches to specific controllers" )
+        # Manually assign mastership to the controller we want
+        roleCall = main.TRUE
+
+        ipList = [ ]
+        deviceList = []
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        try:
+            # Assign mastership to specific controllers. This assignment was
+            # determined for a 7 node cluser, but will work with any sized
+            # cluster
+            for i in range( 1, 29 ):  # switches 1 through 28
+                # set up correct variables:
+                if i == 1:
+                    c = 0
+                    ip = main.nodes[ c ].ip_address  # ONOS1
+                    deviceId = onosCli.getDevice( "1000" ).get( 'id' )
+                elif i == 2:
+                    c = 1 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS2
+                    deviceId = onosCli.getDevice( "2000" ).get( 'id' )
+                elif i == 3:
+                    c = 1 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS2
+                    deviceId = onosCli.getDevice( "3000" ).get( 'id' )
+                elif i == 4:
+                    c = 3 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS4
+                    deviceId = onosCli.getDevice( "3004" ).get( 'id' )
+                elif i == 5:
+                    c = 2 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS3
+                    deviceId = onosCli.getDevice( "5000" ).get( 'id' )
+                elif i == 6:
+                    c = 2 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS3
+                    deviceId = onosCli.getDevice( "6000" ).get( 'id' )
+                elif i == 7:
+                    c = 5 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS6
+                    deviceId = onosCli.getDevice( "6007" ).get( 'id' )
+                elif i >= 8 and i <= 17:
+                    c = 4 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS5
+                    dpid = '3' + str( i ).zfill( 3 )
+                    deviceId = onosCli.getDevice( dpid ).get( 'id' )
+                elif i >= 18 and i <= 27:
+                    c = 6 % main.numCtrls
+                    ip = main.nodes[ c ].ip_address  # ONOS7
+                    dpid = '6' + str( i ).zfill( 3 )
+                    deviceId = onosCli.getDevice( dpid ).get( 'id' )
+                elif i == 28:
+                    c = 0
+                    ip = main.nodes[ c ].ip_address  # ONOS1
+                    deviceId = onosCli.getDevice( "2800" ).get( 'id' )
+                else:
+                    main.log.error( "You didn't write an else statement for " +
+                                    "switch s" + str( i ) )
+                    roleCall = main.FALSE
+                # Assign switch
+                assert deviceId, "No device id for s" + str( i ) + " in ONOS"
+                # TODO: make this controller dynamic
+                roleCall = roleCall and onosCli.deviceRole( deviceId, ip )
+                ipList.append( ip )
+                deviceList.append( deviceId )
+        except ( AttributeError, AssertionError ):
+            main.log.exception( "Something is wrong with ONOS device view" )
+            main.log.info( onosCli.devices() )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=roleCall,
+            onpass="Re-assigned switch mastership to designated controller",
+            onfail="Something wrong with deviceRole calls" )
+
+        main.step( "Check mastership was correctly assigned" )
+        roleCheck = main.TRUE
+        # NOTE: This is due to the fact that device mastership change is not
+        #       atomic and is actually a multi step process
+        time.sleep( 5 )
+        for i in range( len( ipList ) ):
+            ip = ipList[i]
+            deviceId = deviceList[i]
+            # Check assignment
+            master = onosCli.getRole( deviceId ).get( 'master' )
+            if ip in master:
+                roleCheck = roleCheck and main.TRUE
+            else:
+                roleCheck = roleCheck and main.FALSE
+                main.log.error( "Error, controller " + ip + " is not" +
+                                " master " + "of device " +
+                                str( deviceId ) + ". Master is " +
+                                repr( master ) + "." )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=roleCheck,
+            onpass="Switches were successfully reassigned to designated " +
+                   "controller",
+            onfail="Switches were not successfully reassigned" )
+
+    def CASE3( self, main ):
+        """
+        Assign intents
+        """
+        import time
+        import json
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        try:
+            labels
+        except NameError:
+            main.log.error( "labels not defined, setting to []" )
+            labels = []
+        try:
+            data
+        except NameError:
+            main.log.error( "data not defined, setting to []" )
+            data = []
+        # NOTE: we must reinstall intents until we have a persistant intent
+        #        datastore!
+        main.case( "Adding host Intents" )
+        main.caseExplanation = "Discover hosts by using pingall then " +\
+                                "assign predetermined host-to-host intents." +\
+                                " After installation, check that the intent" +\
+                                " is distributed to all nodes and the state" +\
+                                " is INSTALLED"
+
+        # install onos-app-fwd
+        main.step( "Install reactive forwarding app" )
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        installResults = onosCli.activateApp( "org.onosproject.fwd" )
+        utilities.assert_equals( expect=main.TRUE, actual=installResults,
+                                 onpass="Install fwd successful",
+                                 onfail="Install fwd failed" )
+
+        main.step( "Check app ids" )
+        appCheck = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck = appCheck and t.result
+        if appCheck != main.TRUE:
+            main.log.warn( onosCli.apps() )
+            main.log.warn( onosCli.appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+        main.step( "Discovering Hosts( Via pingall for now )" )
+        # FIXME: Once we have a host discovery mechanism, use that instead
+        # REACTIVE FWD test
+        pingResult = main.FALSE
+        passMsg = "Reactive Pingall test passed"
+        time1 = time.time()
+        pingResult = main.Mininet1.pingall()
+        time2 = time.time()
+        if not pingResult:
+            main.log.warn("First pingall failed. Trying again...")
+            pingResult = main.Mininet1.pingall()
+            passMsg += " on the second try"
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=pingResult,
+            onpass= passMsg,
+            onfail="Reactive Pingall failed, " +
+                   "one or more ping pairs failed" )
+        main.log.info( "Time for pingall: %2f seconds" %
+                       ( time2 - time1 ) )
+        # timeout for fwd flows
+        time.sleep( 11 )
+        # uninstall onos-app-fwd
+        main.step( "Uninstall reactive forwarding app" )
+        node = main.activeNodes[0]
+        uninstallResult = main.CLIs[node].deactivateApp( "org.onosproject.fwd" )
+        utilities.assert_equals( expect=main.TRUE, actual=uninstallResult,
+                                 onpass="Uninstall fwd successful",
+                                 onfail="Uninstall fwd failed" )
+
+        main.step( "Check app ids" )
+        threads = []
+        appCheck2 = main.TRUE
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].appToIDCheck,
+                             name="appToIDCheck-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            appCheck2 = appCheck2 and t.result
+        if appCheck2 != main.TRUE:
+            node = main.activeNodes[0]
+            main.log.warn( main.CLIs[node].apps() )
+            main.log.warn( main.CLIs[node].appIDs() )
+        utilities.assert_equals( expect=main.TRUE, actual=appCheck2,
+                                 onpass="App Ids seem to be correct",
+                                 onfail="Something is wrong with app Ids" )
+
+        main.step( "Add host intents via cli" )
+        intentIds = []
+        # TODO: move the host numbers to params
+        #       Maybe look at all the paths we ping?
+        intentAddResult = True
+        hostResult = main.TRUE
+        for i in range( 8, 18 ):
+            main.log.info( "Adding host intent between h" + str( i ) +
+                           " and h" + str( i + 10 ) )
+            host1 = "00:00:00:00:00:" + \
+                str( hex( i )[ 2: ] ).zfill( 2 ).upper()
+            host2 = "00:00:00:00:00:" + \
+                str( hex( i + 10 )[ 2: ] ).zfill( 2 ).upper()
+            # NOTE: getHost can return None
+            host1Dict = onosCli.getHost( host1 )
+            host2Dict = onosCli.getHost( host2 )
+            host1Id = None
+            host2Id = None
+            if host1Dict and host2Dict:
+                host1Id = host1Dict.get( 'id', None )
+                host2Id = host2Dict.get( 'id', None )
+            if host1Id and host2Id:
+                nodeNum = ( i % len( main.activeNodes ) )
+                node = main.activeNodes[nodeNum]
+                tmpId = main.CLIs[node].addHostIntent( host1Id, host2Id )
+                if tmpId:
+                    main.log.info( "Added intent with id: " + tmpId )
+                    intentIds.append( tmpId )
+                else:
+                    main.log.error( "addHostIntent returned: " +
+                                     repr( tmpId ) )
+            else:
+                main.log.error( "Error, getHost() failed for h" + str( i ) +
+                                " and/or h" + str( i + 10 ) )
+                node = main.activeNodes[0]
+                hosts = main.CLIs[node].hosts()
+                main.log.warn( "Hosts output: " )
+                try:
+                    main.log.warn( json.dumps( json.loads( hosts ),
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( repr( hosts ) )
+                hostResult = main.FALSE
+        utilities.assert_equals( expect=main.TRUE, actual=hostResult,
+                                 onpass="Found a host id for each host",
+                                 onfail="Error looking up host ids" )
+
+        intentStart = time.time()
+        onosIds = onosCli.getAllIntentsId()
+        main.log.info( "Submitted intents: " + str( intentIds ) )
+        main.log.info( "Intents in ONOS: " + str( onosIds ) )
+        for intent in intentIds:
+            if intent in onosIds:
+                pass  # intent submitted is in onos
+            else:
+                intentAddResult = False
+        if intentAddResult:
+            intentStop = time.time()
+        else:
+            intentStop = None
+        # Print the intent states
+        intents = onosCli.intents()
+        intentStates = []
+        installedCheck = True
+        main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+        count = 0
+        try:
+            for intent in json.loads( intents ):
+                state = intent.get( 'state', None )
+                if "INSTALLED" not in state:
+                    installedCheck = False
+                intentId = intent.get( 'id', None )
+                intentStates.append( ( intentId, state ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing intents" )
+        # add submitted intents not in the store
+        tmplist = [ i for i, s in intentStates ]
+        missingIntents = False
+        for i in intentIds:
+            if i not in tmplist:
+                intentStates.append( ( i, " - " ) )
+                missingIntents = True
+        intentStates.sort()
+        for i, s in intentStates:
+            count += 1
+            main.log.info( "%-6s%-15s%-15s" %
+                           ( str( count ), str( i ), str( s ) ) )
+        leaders = onosCli.leaders()
+        try:
+            missing = False
+            if leaders:
+                parsedLeaders = json.loads( leaders )
+                main.log.warn( json.dumps( parsedLeaders,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # check for all intent partitions
+                topics = []
+                for i in range( 14 ):
+                    topics.append( "intent-partition-" + str( i ) )
+                main.log.debug( topics )
+                ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                for topic in topics:
+                    if topic not in ONOStopics:
+                        main.log.error( "Error: " + topic +
+                                        " not in leaders" )
+                        missing = True
+            else:
+                main.log.error( "leaders() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing leaders" )
+            main.log.error( repr( leaders ) )
+        # Check all nodes
+        if missing:
+            for i in main.activeNodes:
+                response = main.CLIs[i].leaders( jsonFormat=False)
+                main.log.warn( str( main.CLIs[i].name ) + " leaders output: \n" +
+                               str( response ) )
+
+        partitions = onosCli.partitions()
+        try:
+            if partitions :
+                parsedPartitions = json.loads( partitions )
+                main.log.warn( json.dumps( parsedPartitions,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check for a leader in all paritions
+                # TODO check for consistency among nodes
+            else:
+                main.log.error( "partitions() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing partitions" )
+            main.log.error( repr( partitions ) )
+        pendingMap = onosCli.pendingMap()
+        try:
+            if pendingMap :
+                parsedPending = json.loads( pendingMap )
+                main.log.warn( json.dumps( parsedPending,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check something here?
+            else:
+                main.log.error( "pendingMap() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing pending map" )
+            main.log.error( repr( pendingMap ) )
+
+        intentAddResult = bool( intentAddResult and not missingIntents and
+                                installedCheck )
+        if not intentAddResult:
+            main.log.error( "Error in pushing host intents to ONOS" )
+
+        main.step( "Intent Anti-Entropy dispersion" )
+        for j in range(100):
+            correct = True
+            main.log.info( "Submitted intents: " + str( sorted( intentIds ) ) )
+            for i in main.activeNodes:
+                onosIds = []
+                ids = main.CLIs[i].getAllIntentsId()
+                onosIds.append( ids )
+                main.log.debug( "Intents in " + main.CLIs[i].name + ": " +
+                                str( sorted( onosIds ) ) )
+                if sorted( ids ) != sorted( intentIds ):
+                    main.log.warn( "Set of intent IDs doesn't match" )
+                    correct = False
+                    break
+                else:
+                    intents = json.loads( main.CLIs[i].intents() )
+                    for intent in intents:
+                        if intent[ 'state' ] != "INSTALLED":
+                            main.log.warn( "Intent " + intent[ 'id' ] +
+                                           " is " + intent[ 'state' ] )
+                            correct = False
+                            break
+            if correct:
+                break
+            else:
+                time.sleep(1)
+        if not intentStop:
+            intentStop = time.time()
+        global gossipTime
+        gossipTime = intentStop - intentStart
+        main.log.info( "It took about " + str( gossipTime ) +
+                        " seconds for all intents to appear in each node" )
+        append = False
+        title = "Gossip Intents"
+        count = 1
+        while append is False:
+            curTitle = title + str( count )
+            if curTitle not in labels:
+                labels.append( curTitle )
+                data.append( str( gossipTime ) )
+                append = True
+            else:
+                count += 1
+        gossipPeriod = int( main.params['timers']['gossip'] )
+        maxGossipTime = gossipPeriod * len( main.activeNodes )
+        utilities.assert_greater_equals(
+                expect=maxGossipTime, actual=gossipTime,
+                onpass="ECM anti-entropy for intents worked within " +
+                       "expected time",
+                onfail="Intent ECM anti-entropy took too long. " +
+                       "Expected time:{}, Actual time:{}".format( maxGossipTime,
+                                                                  gossipTime ) )
+        if gossipTime <= maxGossipTime:
+            intentAddResult = True
+
+        if not intentAddResult or "key" in pendingMap:
+            import time
+            installedCheck = True
+            main.log.info( "Sleeping 60 seconds to see if intents are found" )
+            time.sleep( 60 )
+            onosIds = onosCli.getAllIntentsId()
+            main.log.info( "Submitted intents: " + str( intentIds ) )
+            main.log.info( "Intents in ONOS: " + str( onosIds ) )
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            try:
+                for intent in json.loads( intents ):
+                    # Iter through intents of a node
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents" )
+            # add submitted intents not in the store
+            tmplist = [ i for i, s in intentStates ]
+            for i in intentIds:
+                if i not in tmplist:
+                    intentStates.append( ( i, " - " ) )
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            leaders = onosCli.leaders()
+            try:
+                missing = False
+                if leaders:
+                    parsedLeaders = json.loads( leaders )
+                    main.log.warn( json.dumps( parsedLeaders,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # check for all intent partitions
+                    # check for election
+                    topics = []
+                    for i in range( 14 ):
+                        topics.append( "intent-partition-" + str( i ) )
+                    # FIXME: this should only be after we start the app
+                    topics.append( "org.onosproject.election" )
+                    main.log.debug( topics )
+                    ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                    for topic in topics:
+                        if topic not in ONOStopics:
+                            main.log.error( "Error: " + topic +
+                                            " not in leaders" )
+                            missing = True
+                else:
+                    main.log.error( "leaders() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing leaders" )
+                main.log.error( repr( leaders ) )
+            # Check all nodes
+            if missing:
+                for i in main.activeNodes:
+                    node = main.CLIs[i]
+                    response = node.leaders( jsonFormat=False)
+                    main.log.warn( str( node.name ) + " leaders output: \n" +
+                                   str( response ) )
+
+            partitions = onosCli.partitions()
+            try:
+                if partitions :
+                    parsedPartitions = json.loads( partitions )
+                    main.log.warn( json.dumps( parsedPartitions,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check for a leader in all paritions
+                    # TODO check for consistency among nodes
+                else:
+                    main.log.error( "partitions() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing partitions" )
+                main.log.error( repr( partitions ) )
+            pendingMap = onosCli.pendingMap()
+            try:
+                if pendingMap :
+                    parsedPending = json.loads( pendingMap )
+                    main.log.warn( json.dumps( parsedPending,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check something here?
+                else:
+                    main.log.error( "pendingMap() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing pending map" )
+                main.log.error( repr( pendingMap ) )
+
+    def CASE4( self, main ):
+        """
+        Ping across added host intents
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        main.case( "Verify connectivity by sending traffic across Intents" )
+        main.caseExplanation = "Ping across added host intents to check " +\
+                                "functionality and check the state of " +\
+                                "the intent"
+
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        main.step( "Check Intent state" )
+        installedCheck = False
+        loopCount = 0
+        while not installedCheck and loopCount < 40:
+            installedCheck = True
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( intents ):
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents." )
+            # Print states
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            if not installedCheck:
+                time.sleep( 1 )
+                loopCount += 1
+        utilities.assert_equals( expect=True, actual=installedCheck,
+                                 onpass="Intents are all INSTALLED",
+                                 onfail="Intents are not all in " +
+                                        "INSTALLED state" )
+
+        main.step( "Ping across added host intents" )
+        PingResult = main.TRUE
+        for i in range( 8, 18 ):
+            ping = main.Mininet1.pingHost( src="h" + str( i ),
+                                           target="h" + str( i + 10 ) )
+            PingResult = PingResult and ping
+            if ping == main.FALSE:
+                main.log.warn( "Ping failed between h" + str( i ) +
+                               " and h" + str( i + 10 ) )
+            elif ping == main.TRUE:
+                main.log.info( "Ping test passed!" )
+                # Don't set PingResult or you'd override failures
+        if PingResult == main.FALSE:
+            main.log.error(
+                "Intents have not been installed correctly, pings failed." )
+            # TODO: pretty print
+            main.log.warn( "ONOS1 intents: " )
+            try:
+                tmpIntents = onosCli.intents()
+                main.log.warn( json.dumps( json.loads( tmpIntents ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+            except ( ValueError, TypeError ):
+                main.log.warn( repr( tmpIntents ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=PingResult,
+            onpass="Intents have been installed correctly and pings work",
+            onfail="Intents have not been installed correctly, pings failed." )
+
+        main.step( "Check leadership of topics" )
+        leaders = onosCli.leaders()
+        topicCheck = main.TRUE
+        try:
+            if leaders:
+                parsedLeaders = json.loads( leaders )
+                main.log.warn( json.dumps( parsedLeaders,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # check for all intent partitions
+                # check for election
+                # TODO: Look at Devices as topics now that it uses this system
+                topics = []
+                for i in range( 14 ):
+                    topics.append( "intent-partition-" + str( i ) )
+                # FIXME: this should only be after we start the app
+                # FIXME: topics.append( "org.onosproject.election" )
+                # Print leaders output
+                main.log.debug( topics )
+                ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                for topic in topics:
+                    if topic not in ONOStopics:
+                        main.log.error( "Error: " + topic +
+                                        " not in leaders" )
+                        topicCheck = main.FALSE
+            else:
+                main.log.error( "leaders() returned None" )
+                topicCheck = main.FALSE
+        except ( ValueError, TypeError ):
+            topicCheck = main.FALSE
+            main.log.exception( "Error parsing leaders" )
+            main.log.error( repr( leaders ) )
+            # TODO: Check for a leader of these topics
+        # Check all nodes
+        if topicCheck:
+            for i in main.activeNodes:
+                node = main.CLIs[i]
+                response = node.leaders( jsonFormat=False)
+                main.log.warn( str( node.name ) + " leaders output: \n" +
+                               str( response ) )
+
+        utilities.assert_equals( expect=main.TRUE, actual=topicCheck,
+                                 onpass="intent Partitions is in leaders",
+                                 onfail="Some topics were lost " )
+        # Print partitions
+        partitions = onosCli.partitions()
+        try:
+            if partitions :
+                parsedPartitions = json.loads( partitions )
+                main.log.warn( json.dumps( parsedPartitions,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check for a leader in all paritions
+                # TODO check for consistency among nodes
+            else:
+                main.log.error( "partitions() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing partitions" )
+            main.log.error( repr( partitions ) )
+        # Print Pending Map
+        pendingMap = onosCli.pendingMap()
+        try:
+            if pendingMap :
+                parsedPending = json.loads( pendingMap )
+                main.log.warn( json.dumps( parsedPending,
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+                # TODO check something here?
+            else:
+                main.log.error( "pendingMap() returned None" )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing pending map" )
+            main.log.error( repr( pendingMap ) )
+
+        if not installedCheck:
+            main.log.info( "Waiting 60 seconds to see if the state of " +
+                           "intents change" )
+            time.sleep( 60 )
+            # Print the intent states
+            intents = onosCli.intents()
+            intentStates = []
+            main.log.info( "%-6s%-15s%-15s" % ( 'Count', 'ID', 'State' ) )
+            count = 0
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( intents ):
+                    state = intent.get( 'state', None )
+                    if "INSTALLED" not in state:
+                        installedCheck = False
+                    intentId = intent.get( 'id', None )
+                    intentStates.append( ( intentId, state ) )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing intents." )
+            intentStates.sort()
+            for i, s in intentStates:
+                count += 1
+                main.log.info( "%-6s%-15s%-15s" %
+                               ( str( count ), str( i ), str( s ) ) )
+            leaders = onosCli.leaders()
+            try:
+                missing = False
+                if leaders:
+                    parsedLeaders = json.loads( leaders )
+                    main.log.warn( json.dumps( parsedLeaders,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # check for all intent partitions
+                    # check for election
+                    topics = []
+                    for i in range( 14 ):
+                        topics.append( "intent-partition-" + str( i ) )
+                    # FIXME: this should only be after we start the app
+                    topics.append( "org.onosproject.election" )
+                    main.log.debug( topics )
+                    ONOStopics = [ j['topic'] for j in parsedLeaders ]
+                    for topic in topics:
+                        if topic not in ONOStopics:
+                            main.log.error( "Error: " + topic +
+                                            " not in leaders" )
+                            missing = True
+                else:
+                    main.log.error( "leaders() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing leaders" )
+                main.log.error( repr( leaders ) )
+            if missing:
+                for i in main.activeNodes:
+                    node = main.CLIs[i]
+                    response = node.leaders( jsonFormat=False)
+                    main.log.warn( str( node.name ) + " leaders output: \n" +
+                                   str( response ) )
+
+            partitions = onosCli.partitions()
+            try:
+                if partitions :
+                    parsedPartitions = json.loads( partitions )
+                    main.log.warn( json.dumps( parsedPartitions,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check for a leader in all paritions
+                    # TODO check for consistency among nodes
+                else:
+                    main.log.error( "partitions() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing partitions" )
+                main.log.error( repr( partitions ) )
+            pendingMap = onosCli.pendingMap()
+            try:
+                if pendingMap :
+                    parsedPending = json.loads( pendingMap )
+                    main.log.warn( json.dumps( parsedPending,
+                                               sort_keys=True,
+                                               indent=4,
+                                               separators=( ',', ': ' ) ) )
+                    # TODO check something here?
+                else:
+                    main.log.error( "pendingMap() returned None" )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error parsing pending map" )
+                main.log.error( repr( pendingMap ) )
+        # Print flowrules
+        node = main.activeNodes[0]
+        main.log.debug( main.CLIs[node].flows( jsonFormat=False ) )
+        main.step( "Wait a minute then ping again" )
+        # the wait is above
+        PingResult = main.TRUE
+        for i in range( 8, 18 ):
+            ping = main.Mininet1.pingHost( src="h" + str( i ),
+                                           target="h" + str( i + 10 ) )
+            PingResult = PingResult and ping
+            if ping == main.FALSE:
+                main.log.warn( "Ping failed between h" + str( i ) +
+                               " and h" + str( i + 10 ) )
+            elif ping == main.TRUE:
+                main.log.info( "Ping test passed!" )
+                # Don't set PingResult or you'd override failures
+        if PingResult == main.FALSE:
+            main.log.error(
+                "Intents have not been installed correctly, pings failed." )
+            # TODO: pretty print
+            main.log.warn( "ONOS1 intents: " )
+            try:
+                tmpIntents = onosCli.intents()
+                main.log.warn( json.dumps( json.loads( tmpIntents ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+            except ( ValueError, TypeError ):
+                main.log.warn( repr( tmpIntents ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=PingResult,
+            onpass="Intents have been installed correctly and pings work",
+            onfail="Intents have not been installed correctly, pings failed." )
+
+    def CASE5( self, main ):
+        """
+        Reading state of ONOS
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Setting up and gathering data for current state" )
+        # The general idea for this test case is to pull the state of
+        # ( intents,flows, topology,... ) from each ONOS node
+        # We can then compare them with each other and also with past states
+
+        main.step( "Check that each switch has a master" )
+        global mastershipState
+        mastershipState = '[]'
+
+        # Assert that each device has a master
+        rolesNotNull = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].rolesNotNull,
+                             name="rolesNotNull-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            rolesNotNull = rolesNotNull and t.result
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=rolesNotNull,
+            onpass="Each device has a master",
+            onfail="Some devices don't have a master assigned" )
+
+        main.step( "Get the Mastership of each switch from each controller" )
+        ONOSMastership = []
+        consistentMastership = True
+        rolesResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].roles,
+                             name="roles-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSMastership.append( t.result )
+
+        for i in range( len( ONOSMastership ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSMastership[i] or "Error" in ONOSMastership[i]:
+                main.log.error( "Error in getting ONOS" + node + " roles" )
+                main.log.warn( "ONOS" + node + " mastership response: " +
+                               repr( ONOSMastership[i] ) )
+                rolesResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=rolesResults,
+            onpass="No error in reading roles output",
+            onfail="Error in reading roles from ONOS" )
+
+        main.step( "Check for consistency in roles from each controller" )
+        if all([ i == ONOSMastership[ 0 ] for i in ONOSMastership ] ):
+            main.log.info(
+                "Switch roles are consistent across all ONOS nodes" )
+        else:
+            consistentMastership = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentMastership,
+            onpass="Switch roles are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of switch roles" )
+
+        if rolesResults and not consistentMastership:
+            for i in range( len( main.activeNodes ) ):
+                node = str( main.activeNodes[i] + 1 )
+                try:
+                    main.log.warn(
+                        "ONOS" + node + " roles: ",
+                        json.dumps(
+                            json.loads( ONOSMastership[ i ] ),
+                            sort_keys=True,
+                            indent=4,
+                            separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( repr( ONOSMastership[ i ] ) )
+        elif rolesResults and consistentMastership:
+            mastershipState = ONOSMastership[ 0 ]
+
+        main.step( "Get the intents from each controller" )
+        global intentState
+        intentState = []
+        ONOSIntents = []
+        consistentIntents = True  # Are Intents consistent across nodes?
+        intentsResults = True  # Could we read Intents from ONOS?
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].intents,
+                             name="intents-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSIntents.append( t.result )
+
+        for i in range( len( ONOSIntents ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSIntents[ i ] or "Error" in ONOSIntents[ i ]:
+                main.log.error( "Error in getting ONOS" + node + " intents" )
+                main.log.warn( "ONOS" + node + " intents response: " +
+                               repr( ONOSIntents[ i ] ) )
+                intentsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=intentsResults,
+            onpass="No error in reading intents output",
+            onfail="Error in reading intents from ONOS" )
+
+        main.step( "Check for consistency in Intents from each controller" )
+        if all([ sorted( i ) == sorted( ONOSIntents[ 0 ] ) for i in ONOSIntents ] ):
+            main.log.info( "Intents are consistent across all ONOS " +
+                             "nodes" )
+        else:
+            consistentIntents = False
+            main.log.error( "Intents not consistent" )
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentIntents,
+            onpass="Intents are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of intents" )
+
+        if intentsResults:
+            # Try to make it easy to figure out what is happening
+            #
+            # Intent      ONOS1      ONOS2    ...
+            #  0x01     INSTALLED  INSTALLING
+            #  ...        ...         ...
+            #  ...        ...         ...
+            title = "   Id"
+            for n in main.activeNodes:
+                title += " " * 10 + "ONOS" + str( n + 1 )
+            main.log.warn( title )
+            # get all intent keys in the cluster
+            keys = []
+            try:
+                # Get the set of all intent keys
+                for nodeStr in ONOSIntents:
+                    node = json.loads( nodeStr )
+                    for intent in node:
+                        keys.append( intent.get( 'id' ) )
+                keys = set( keys )
+                # For each intent key, print the state on each node
+                for key in keys:
+                    row = "%-13s" % key
+                    for nodeStr in ONOSIntents:
+                        node = json.loads( nodeStr )
+                        for intent in node:
+                            if intent.get( 'id', "Error" ) == key:
+                                row += "%-15s" % intent.get( 'state' )
+                    main.log.warn( row )
+                # End of intent state table
+            except ValueError as e:
+                main.log.exception( e )
+                main.log.debug( "nodeStr was: " + repr( nodeStr ) )
+
+        if intentsResults and not consistentIntents:
+            # print the json objects
+            n = str( main.activeNodes[-1] + 1 )
+            main.log.debug( "ONOS" + n + " intents: " )
+            main.log.debug( json.dumps( json.loads( ONOSIntents[ -1 ] ),
+                                        sort_keys=True,
+                                        indent=4,
+                                        separators=( ',', ': ' ) ) )
+            for i in range( len( ONOSIntents ) ):
+                node = str( main.activeNodes[i] + 1 )
+                if ONOSIntents[ i ] != ONOSIntents[ -1 ]:
+                    main.log.debug( "ONOS" + node + " intents: " )
+                    main.log.debug( json.dumps( json.loads( ONOSIntents[i] ),
+                                                sort_keys=True,
+                                                indent=4,
+                                                separators=( ',', ': ' ) ) )
+                else:
+                    main.log.debug( "ONOS" + node + " intents match ONOS" +
+                                    n + " intents" )
+        elif intentsResults and consistentIntents:
+            intentState = ONOSIntents[ 0 ]
+
+        main.step( "Get the flows from each controller" )
+        global flowState
+        flowState = []
+        ONOSFlows = []
+        ONOSFlowsJson = []
+        flowCheck = main.FALSE
+        consistentFlows = True
+        flowsResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].flows,
+                             name="flows-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        # NOTE: Flows command can take some time to run
+        time.sleep(30)
+        for t in threads:
+            t.join()
+            result = t.result
+            ONOSFlows.append( result )
+
+        for i in range( len( ONOSFlows ) ):
+            num = str( main.activeNodes[i] + 1 )
+            if not ONOSFlows[ i ] or "Error" in ONOSFlows[ i ]:
+                main.log.error( "Error in getting ONOS" + num + " flows" )
+                main.log.warn( "ONOS" + num + " flows response: " +
+                               repr( ONOSFlows[ i ] ) )
+                flowsResults = False
+                ONOSFlowsJson.append( None )
+            else:
+                try:
+                    ONOSFlowsJson.append( json.loads( ONOSFlows[ i ] ) )
+                except ( ValueError, TypeError ):
+                    # FIXME: change this to log.error?
+                    main.log.exception( "Error in parsing ONOS" + num +
+                                        " response as json." )
+                    main.log.error( repr( ONOSFlows[ i ] ) )
+                    ONOSFlowsJson.append( None )
+                    flowsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=flowsResults,
+            onpass="No error in reading flows output",
+            onfail="Error in reading flows from ONOS" )
+
+        main.step( "Check for consistency in Flows from each controller" )
+        tmp = [ len( i ) == len( ONOSFlowsJson[ 0 ] ) for i in ONOSFlowsJson ]
+        if all( tmp ):
+            main.log.info( "Flow count is consistent across all ONOS nodes" )
+        else:
+            consistentFlows = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentFlows,
+            onpass="The flow count is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different flow counts" )
+
+        if flowsResults and not consistentFlows:
+            for i in range( len( ONOSFlows ) ):
+                node = str( main.activeNodes[i] + 1 )
+                try:
+                    main.log.warn(
+                        "ONOS" + node + " flows: " +
+                        json.dumps( json.loads( ONOSFlows[i] ), sort_keys=True,
+                                    indent=4, separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.warn( "ONOS" + node + " flows: " +
+                                   repr( ONOSFlows[ i ] ) )
+        elif flowsResults and consistentFlows:
+            flowCheck = main.TRUE
+            flowState = ONOSFlows[ 0 ]
+
+        main.step( "Get the OF Table entries" )
+        global flows
+        flows = []
+        for i in range( 1, 29 ):
+            flows.append( main.Mininet1.getFlowTable( "s" + str( i ), version="1.3", debug=False ) )
+        if flowCheck == main.FALSE:
+            for table in flows:
+                main.log.warn( table )
+        # TODO: Compare switch flow tables with ONOS flow tables
+
+        main.step( "Start continuous pings" )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source1' ],
+            target=main.params[ 'PING' ][ 'target1' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source2' ],
+            target=main.params[ 'PING' ][ 'target2' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source3' ],
+            target=main.params[ 'PING' ][ 'target3' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source4' ],
+            target=main.params[ 'PING' ][ 'target4' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source5' ],
+            target=main.params[ 'PING' ][ 'target5' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source6' ],
+            target=main.params[ 'PING' ][ 'target6' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source7' ],
+            target=main.params[ 'PING' ][ 'target7' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source8' ],
+            target=main.params[ 'PING' ][ 'target8' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source9' ],
+            target=main.params[ 'PING' ][ 'target9' ],
+            pingTime=500 )
+        main.Mininet2.pingLong(
+            src=main.params[ 'PING' ][ 'source10' ],
+            target=main.params[ 'PING' ][ 'target10' ],
+            pingTime=500 )
+
+        main.step( "Collecting topology information from ONOS" )
+        devices = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        hosts = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].hosts,
+                             name="hosts-" + str( i ),
+                             args=[ ] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            try:
+                hosts.append( json.loads( t.result ) )
+            except ( ValueError, TypeError ):
+                # FIXME: better handling of this, print which node
+                #        Maybe use thread name?
+                main.log.exception( "Error parsing json output of hosts" )
+                main.log.warn( repr( t.result ) )
+                hosts.append( None )
+
+        ports = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        links = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        clusters = []
+        threads = []
+        for i in main.activeNodes:
+            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 )
+        # Compare json objects for hosts and dataplane clusters
+
+        # hosts
+        main.step( "Host view is consistent across ONOS nodes" )
+        consistentHostsResult = main.TRUE
+        for controller in range( len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ] and "Error" not in hosts[ controller ]:
+                if hosts[ controller ] == hosts[ 0 ]:
+                    continue
+                else:  # hosts not consistent
+                    main.log.error( "hosts from ONOS" +
+                                     controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    main.log.warn( repr( hosts[ controller ] ) )
+                    consistentHostsResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting ONOS hosts from ONOS" +
+                                 controllerStr )
+                consistentHostsResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " hosts response: " +
+                               repr( hosts[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentHostsResult,
+            onpass="Hosts view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of hosts" )
+
+        main.step( "Each host has an IP address" )
+        ipResult = main.TRUE
+        for controller in range( 0, len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ]:
+                for host in hosts[ controller ]:
+                    if not host.get( 'ipAddresses', [ ] ):
+                        main.log.error( "Error with host ips on controller" +
+                                        controllerStr + ": " + str( host ) )
+                        ipResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=ipResult,
+            onpass="The ips of the hosts aren't empty",
+            onfail="The ip of at least one host is missing" )
+
+        # Strongly connected clusters of devices
+        main.step( "Cluster view is consistent across ONOS nodes" )
+        consistentClustersResult = main.TRUE
+        for controller in range( len( clusters ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if "Error" not in clusters[ controller ]:
+                if clusters[ controller ] == clusters[ 0 ]:
+                    continue
+                else:  # clusters not consistent
+                    main.log.error( "clusters from ONOS" + controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    consistentClustersResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting dataplane clusters " +
+                                 "from ONOS" + controllerStr )
+                consistentClustersResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " clusters response: " +
+                               repr( clusters[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentClustersResult,
+            onpass="Clusters view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
+
+        # there should always only be one cluster
+        main.step( "Cluster view correct across ONOS nodes" )
+        try:
+            numClusters = len( json.loads( clusters[ 0 ] ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing clusters[0]: " +
+                                repr( clusters[ 0 ] ) )
+            numClusters = "ERROR"
+        utilities.assert_equals(
+            expect=1,
+            actual=numClusters,
+            onpass="ONOS shows 1 SCC",
+            onfail="ONOS shows " + str( numClusters ) + " SCCs" )
+
+        main.step( "Comparing ONOS topology to MN" )
+        devicesResults = main.TRUE
+        linksResults = main.TRUE
+        hostsResults = main.TRUE
+        mnSwitches = main.Mininet1.getSwitches()
+        mnLinks = main.Mininet1.getLinks()
+        mnHosts = main.Mininet1.getHosts()
+        for controller in main.activeNodes:
+            controllerStr = str( main.activeNodes[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 ] and "Error" not in hosts[ controller ]:
+                currentHostsResult = main.Mininet1.compareHosts(
+                        mnHosts,
+                        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" )
+
+            devicesResults = devicesResults and currentDevicesResult
+            linksResults = linksResults and currentLinksResult
+            hostsResults = hostsResults and currentHostsResult
+
+        main.step( "Device information is correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=devicesResults,
+            onpass="Device information is correct",
+            onfail="Device information is incorrect" )
+
+        main.step( "Links are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=linksResults,
+            onpass="Link are correct",
+            onfail="Links are incorrect" )
+
+        main.step( "Hosts are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Hosts are correct",
+            onfail="Hosts are incorrect" )
+
+    def CASE6( self, main ):
+        """
+        The Scaling case.
+        """
+        import time
+        import re
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        try:
+            labels
+        except NameError:
+            main.log.error( "labels not defined, setting to []" )
+            global labels
+            labels = []
+        try:
+            data
+        except NameError:
+            main.log.error( "data not defined, setting to []" )
+            global data
+            data = []
+
+        main.case( "Swap some of the ONOS nodes" )
+
+        main.step( "Checking ONOS Logs for errors" )
+        for i in main.activeNodes:
+            node = main.nodes[i]
+            main.log.debug( "Checking logs for errors on " + node.name + ":" )
+            main.log.warn( main.ONOSbench.checkLogs( node.ip_address ) )
+
+        main.step( "Generate new metadata file" )
+        old = [ main.activeNodes[0],  main.activeNodes[-1] ]
+        new = range( main.ONOSbench.maxNodes )[-2:]
+        assert len( old ) == len( new ), "Length of nodes to swap don't match"
+        handle = main.ONOSbench.handle
+        for x, y in zip( old, new ):
+            handle.sendline( "export OC{}=$OC{}".format( x + 1, y + 1 ) )
+            handle.expect( "\$" )  # from the variable
+            ret = handle.before
+            handle.expect( "\$" )  # From the prompt
+            ret += handle.before
+            main.log.debug( ret )
+            main.activeNodes.remove( x )
+            main.activeNodes.append( y )
+
+        genResult = main.Server.generateFile( main.numCtrls )
+        utilities.assert_equals( expect=main.TRUE, actual=genResult,
+                                 onpass="New cluster metadata file generated",
+                                 onfail="Failled to generate new metadata file" )
+        time.sleep( 5 )  # Give time for nodes to read new file
+
+        main.step( "Start new nodes" )  # OR stop old nodes?
+        started = main.TRUE
+        for i in new:
+            started = main.ONOSbench.onosStart( main.nodes[i].ip_address ) and main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=started,
+                                 onpass="ONOS started",
+                                 onfail="ONOS start NOT successful" )
+
+        main.step( "Checking if ONOS is up yet" )
+        for i in range( 2 ):
+            onosIsupResult = main.TRUE
+            for i in main.activeNodes:
+                node = main.nodes[i]
+                started = main.ONOSbench.isup( node.ip_address )
+                if not started:
+                    main.log.error( node.name + " didn't start!" )
+                onosIsupResult = onosIsupResult and started
+            if onosIsupResult == main.TRUE:
+                break
+        utilities.assert_equals( expect=main.TRUE, actual=onosIsupResult,
+                                 onpass="ONOS started",
+                                 onfail="ONOS start NOT successful" )
+
+        main.log.step( "Starting ONOS CLI sessions" )
+        cliResults = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].startOnosCli,
+                             name="startOnosCli-" + str( i ),
+                             args=[main.nodes[i].ip_address] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            cliResults = cliResults and t.result
+        utilities.assert_equals( expect=main.TRUE, actual=cliResults,
+                                 onpass="ONOS cli started",
+                                 onfail="ONOS clis did not start" )
+
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+
+        for i in range( 10 ):
+            ready = True
+            for i in main.activeNodes:
+                cli = main.CLIs[i]
+                output = cli.summary()
+                if not output:
+                    ready = False
+            if ready:
+                break
+            time.sleep( 30 )
+        utilities.assert_equals( expect=True, actual=ready,
+                                 onpass="ONOS summary command succeded",
+                                 onfail="ONOS summary command failed" )
+        if not ready:
+            main.cleanup()
+            main.exit()
+
+        # Rerun for election on new nodes
+        runResults = main.TRUE
+        for i in main.activeNodes:
+            cli = main.CLIs[i]
+            run = cli.electionTestRun()
+            if run != main.TRUE:
+                main.log.error( "Error running for election on " + cli.name )
+            runResults = runResults and run
+        utilities.assert_equals( expect=main.TRUE, actual=runResults,
+                                 onpass="Reran for election",
+                                 onfail="Failed to rerun for election" )
+
+        for node in main.activeNodes:
+            main.log.warn( "\n****************** {} **************".format( main.nodes[node].ip_address ) )
+            main.log.debug( main.CLIs[node].nodes( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].leaders( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].partitions( jsonFormat=False ) )
+            main.log.debug( main.CLIs[node].apps( jsonFormat=False ) )
+
+        main.step( "Reapplying cell variable to environment" )
+        cellName = main.params[ 'ENV' ][ 'cellName' ]
+        cellResult = main.ONOSbench.setCell( cellName )
+        utilities.assert_equals( expect=main.TRUE, actual=cellResult,
+                                 onpass="Set cell successfull",
+                                 onfail="Failled to set cell" )
+
+    def CASE7( self, main ):
+        """
+        Check state after ONOS scaling
+        """
+        import json
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        main.case( "Running ONOS Constant State Tests" )
+
+        main.step( "Check that each switch has a master" )
+        # Assert that each device has a master
+        rolesNotNull = main.TRUE
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].rolesNotNull,
+                             name="rolesNotNull-" + str( i ),
+                             args=[ ] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            rolesNotNull = rolesNotNull and t.result
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=rolesNotNull,
+            onpass="Each device has a master",
+            onfail="Some devices don't have a master assigned" )
+
+        main.step( "Read device roles from ONOS" )
+        ONOSMastership = []
+        consistentMastership = True
+        rolesResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].roles,
+                             name="roles-" + str( i ),
+                             args=[] )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSMastership.append( t.result )
+
+        for i in range( len( ONOSMastership ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSMastership[i] or "Error" in ONOSMastership[i]:
+                main.log.error( "Error in getting ONOS" + node + " roles" )
+                main.log.warn( "ONOS" + node + " mastership response: " +
+                               repr( ONOSMastership[i] ) )
+                rolesResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=rolesResults,
+            onpass="No error in reading roles output",
+            onfail="Error in reading roles from ONOS" )
+
+        main.step( "Check for consistency in roles from each controller" )
+        if all([ i == ONOSMastership[ 0 ] for i in ONOSMastership ] ):
+            main.log.info(
+                "Switch roles are consistent across all ONOS nodes" )
+        else:
+            consistentMastership = False
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentMastership,
+            onpass="Switch roles are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of switch roles" )
+
+        if rolesResults and not consistentMastership:
+            for i in range( len( ONOSMastership ) ):
+                node = str( main.activeNodes[i] + 1 )
+                main.log.warn( "ONOS" + node + " roles: ",
+                               json.dumps( json.loads( ONOSMastership[ i ] ),
+                                           sort_keys=True,
+                                           indent=4,
+                                           separators=( ',', ': ' ) ) )
+
+        # NOTE: we expect mastership to change on controller scaling down
+
+        main.step( "Get the intents and compare across all nodes" )
+        ONOSIntents = []
+        intentCheck = main.FALSE
+        consistentIntents = True
+        intentsResults = True
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].intents,
+                             name="intents-" + str( i ),
+                             args=[],
+                             kwargs={ 'jsonFormat': True } )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            ONOSIntents.append( t.result )
+
+        for i in range( len( ONOSIntents) ):
+            node = str( main.activeNodes[i] + 1 )
+            if not ONOSIntents[ i ] or "Error" in ONOSIntents[ i ]:
+                main.log.error( "Error in getting ONOS" + node + " intents" )
+                main.log.warn( "ONOS" + node + " intents response: " +
+                               repr( ONOSIntents[ i ] ) )
+                intentsResults = False
+        utilities.assert_equals(
+            expect=True,
+            actual=intentsResults,
+            onpass="No error in reading intents output",
+            onfail="Error in reading intents from ONOS" )
+
+        main.step( "Check for consistency in Intents from each controller" )
+        if all([ sorted( i ) == sorted( ONOSIntents[ 0 ] ) for i in ONOSIntents ] ):
+            main.log.info( "Intents are consistent across all ONOS " +
+                             "nodes" )
+        else:
+            consistentIntents = False
+
+        # Try to make it easy to figure out what is happening
+        #
+        # Intent      ONOS1      ONOS2    ...
+        #  0x01     INSTALLED  INSTALLING
+        #  ...        ...         ...
+        #  ...        ...         ...
+        title = "   ID"
+        for n in main.activeNodes:
+            title += " " * 10 + "ONOS" + str( n + 1 )
+        main.log.warn( title )
+        # get all intent keys in the cluster
+        keys = []
+        for nodeStr in ONOSIntents:
+            node = json.loads( nodeStr )
+            for intent in node:
+                keys.append( intent.get( 'id' ) )
+        keys = set( keys )
+        for key in keys:
+            row = "%-13s" % key
+            for nodeStr in ONOSIntents:
+                node = json.loads( nodeStr )
+                for intent in node:
+                    if intent.get( 'id' ) == key:
+                        row += "%-15s" % intent.get( 'state' )
+            main.log.warn( row )
+        # End table view
+
+        utilities.assert_equals(
+            expect=True,
+            actual=consistentIntents,
+            onpass="Intents are consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of intents" )
+        intentStates = []
+        for node in ONOSIntents:  # Iter through ONOS nodes
+            nodeStates = []
+            # Iter through intents of a node
+            try:
+                for intent in json.loads( node ):
+                    nodeStates.append( intent[ 'state' ] )
+            except ( ValueError, TypeError ):
+                main.log.exception( "Error in parsing intents" )
+                main.log.error( repr( node ) )
+            intentStates.append( nodeStates )
+            out = [ (i, nodeStates.count( i ) ) for i in set( nodeStates ) ]
+            main.log.info( dict( out ) )
+
+        if intentsResults and not consistentIntents:
+            for i in range( len( main.activeNodes ) ):
+                node = str( main.activeNodes[i] + 1 )
+                main.log.warn( "ONOS" + node + " intents: " )
+                main.log.warn( json.dumps(
+                    json.loads( ONOSIntents[ i ] ),
+                    sort_keys=True,
+                    indent=4,
+                    separators=( ',', ': ' ) ) )
+        elif intentsResults and consistentIntents:
+            intentCheck = main.TRUE
+
+        main.step( "Compare current intents with intents before the scaling" )
+        # NOTE: this requires case 5 to pass for intentState to be set.
+        #      maybe we should stop the test if that fails?
+        sameIntents = main.FALSE
+        try:
+            intentState
+        except NameError:
+            main.log.warn( "No previous intent state was saved" )
+        else:
+            if intentState and intentState == ONOSIntents[ 0 ]:
+                sameIntents = main.TRUE
+                main.log.info( "Intents are consistent with before scaling" )
+            # TODO: possibly the states have changed? we may need to figure out
+            #       what the acceptable states are
+            elif len( intentState ) == len( ONOSIntents[ 0 ] ):
+                sameIntents = main.TRUE
+                try:
+                    before = json.loads( intentState )
+                    after = json.loads( ONOSIntents[ 0 ] )
+                    for intent in before:
+                        if intent not in after:
+                            sameIntents = main.FALSE
+                            main.log.debug( "Intent is not currently in ONOS " +
+                                            "(at least in the same form):" )
+                            main.log.debug( json.dumps( intent ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Exception printing intents" )
+                    main.log.debug( repr( ONOSIntents[0] ) )
+                    main.log.debug( repr( intentState ) )
+            if sameIntents == main.FALSE:
+                try:
+                    main.log.debug( "ONOS intents before: " )
+                    main.log.debug( json.dumps( json.loads( intentState ),
+                                                sort_keys=True, indent=4,
+                                                separators=( ',', ': ' ) ) )
+                    main.log.debug( "Current ONOS intents: " )
+                    main.log.debug( json.dumps( json.loads( ONOSIntents[ 0 ] ),
+                                                sort_keys=True, indent=4,
+                                                separators=( ',', ': ' ) ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Exception printing intents" )
+                    main.log.debug( repr( ONOSIntents[0] ) )
+                    main.log.debug( repr( intentState ) )
+            utilities.assert_equals(
+                expect=main.TRUE,
+                actual=sameIntents,
+                onpass="Intents are consistent with before scaling",
+                onfail="The Intents changed during scaling" )
+        intentCheck = intentCheck and sameIntents
+
+        main.step( "Get the OF Table entries and compare to before " +
+                   "component scaling" )
+        FlowTables = main.TRUE
+        for i in range( 28 ):
+            main.log.info( "Checking flow table on s" + str( i + 1 ) )
+            tmpFlows = main.Mininet1.getFlowTable( "s" + str( i + 1 ), version="1.3", debug=False )
+            curSwitch = main.Mininet1.flowTableComp( flows[i], tmpFlows )
+            FlowTables = FlowTables and curSwitch
+            if curSwitch == main.FALSE:
+                main.log.warn( "Differences in flow table for switch: s{}".format( i + 1 ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=FlowTables,
+            onpass="No changes were found in the flow tables",
+            onfail="Changes were found in the flow tables" )
+
+        main.Mininet2.pingLongKill()
+        '''
+        # main.step( "Check the continuous pings to ensure that no packets " +
+        #            "were dropped during component failure" )
+        main.Mininet2.pingKill( main.params[ 'TESTONUSER' ],
+                                main.params[ 'TESTONIP' ] )
+        LossInPings = main.FALSE
+        # NOTE: checkForLoss returns main.FALSE with 0% packet loss
+        for i in range( 8, 18 ):
+            main.log.info(
+                "Checking for a loss in pings along flow from s" +
+                str( i ) )
+            LossInPings = main.Mininet2.checkForLoss(
+                "/tmp/ping.h" +
+                str( i ) ) or LossInPings
+        if LossInPings == main.TRUE:
+            main.log.info( "Loss in ping detected" )
+        elif LossInPings == main.ERROR:
+            main.log.info( "There are multiple mininet process running" )
+        elif LossInPings == main.FALSE:
+            main.log.info( "No Loss in the pings" )
+            main.log.info( "No loss of dataplane connectivity" )
+        # utilities.assert_equals(
+        #     expect=main.FALSE,
+        #     actual=LossInPings,
+        #     onpass="No Loss of connectivity",
+        #     onfail="Loss of dataplane connectivity detected" )
+
+        # NOTE: Since intents are not persisted with IntnentStore,
+        #       we expect loss in dataplane connectivity
+        LossInPings = main.FALSE
+        '''
+
+        main.step( "Leadership Election is still functional" )
+        # Test of LeadershipElection
+        leaderList = []
+        leaderResult = main.TRUE
+
+        for i in main.activeNodes:
+            cli = main.CLIs[i]
+            leaderN = cli.electionTestLeader()
+            leaderList.append( leaderN )
+            if leaderN == main.FALSE:
+                # error in response
+                main.log.error( "Something is wrong with " +
+                                 "electionTestLeader function, check the" +
+                                 " error logs" )
+                leaderResult = main.FALSE
+            elif leaderN is None:
+                main.log.error( cli.name +
+                                 " shows no leader for the election-app." )
+                leaderResult = main.FALSE
+        if len( set( leaderList ) ) != 1:
+            leaderResult = main.FALSE
+            main.log.error(
+                "Inconsistent view of leader for the election test app" )
+            # TODO: print the list
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=leaderResult,
+            onpass="Leadership election passed",
+            onfail="Something went wrong with Leadership election" )
+
+    def CASE8( self, main ):
+        """
+        Compare topo
+        """
+        import json
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Compare ONOS Topology view to Mininet topology" )
+        main.caseExplanation = "Compare topology objects between Mininet" +\
+                                " and ONOS"
+        topoResult = main.FALSE
+        topoFailMsg = "ONOS topology don't match Mininet"
+        elapsed = 0
+        count = 0
+        main.step( "Comparing ONOS topology to MN topology" )
+        startTime = time.time()
+        # Give time for Gossip to work
+        while topoResult == main.FALSE and ( elapsed < 60 or count < 3 ):
+            devicesResults = main.TRUE
+            linksResults = main.TRUE
+            hostsResults = main.TRUE
+            hostAttachmentResults = True
+            count += 1
+            cliStart = time.time()
+            devices = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="devices-" + str( i ),
+                                 args=[ main.CLIs[i].devices, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                devices.append( t.result )
+            hosts = []
+            ipResult = main.TRUE
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="hosts-" + str( i ),
+                                 args=[ main.CLIs[i].hosts, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                try:
+                    hosts.append( json.loads( t.result ) )
+                except ( ValueError, TypeError ):
+                    main.log.exception( "Error parsing hosts results" )
+                    main.log.error( repr( t.result ) )
+                    hosts.append( None )
+            for controller in range( 0, len( hosts ) ):
+                controllerStr = str( main.activeNodes[controller] + 1 )
+                if hosts[ controller ]:
+                    for host in hosts[ controller ]:
+                        if host is None or host.get( 'ipAddresses', [] ) == []:
+                            main.log.error(
+                                "Error with host ipAddresses on controller" +
+                                controllerStr + ": " + str( host ) )
+                            ipResult = main.FALSE
+            ports = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="ports-" + str( i ),
+                                 args=[ main.CLIs[i].ports, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                ports.append( t.result )
+            links = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="links-" + str( i ),
+                                 args=[ main.CLIs[i].links, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                links.append( t.result )
+            clusters = []
+            threads = []
+            for i in main.activeNodes:
+                t = main.Thread( target=utilities.retry,
+                                 name="clusters-" + str( i ),
+                                 args=[ main.CLIs[i].clusters, [ None ] ],
+                                 kwargs= { 'sleep': 5, 'attempts': 5,
+                                           'randomTime': True } )
+                threads.append( t )
+                t.start()
+
+            for t in threads:
+                t.join()
+                clusters.append( t.result )
+
+            elapsed = time.time() - startTime
+            cliTime = time.time() - cliStart
+            print "Elapsed time: " + str( elapsed )
+            print "CLI time: " + str( cliTime )
+
+            if all( e is None for e in devices ) and\
+               all( e is None for e in hosts ) and\
+               all( e is None for e in ports ) and\
+               all( e is None for e in links ) and\
+               all( e is None for e in clusters ):
+                   topoFailMsg = "Could not get topology from ONOS"
+                   main.log.error( topoFailMsg )
+                   continue  # Try again, No use trying to compare
+
+            mnSwitches = main.Mininet1.getSwitches()
+            mnLinks = main.Mininet1.getLinks()
+            mnHosts = main.Mininet1.getHosts()
+            for controller in range( len( main.activeNodes ) ):
+                controllerStr = str( main.activeNodes[controller] + 1 )
+                if devices[ controller ] and ports[ controller ] and\
+                    "Error" not in devices[ controller ] and\
+                    "Error" not in ports[ controller ]:
+
+                    try:
+                        currentDevicesResult = main.Mininet1.compareSwitches(
+                                mnSwitches,
+                                json.loads( devices[ controller ] ),
+                                json.loads( ports[ controller ] ) )
+                    except ( TypeError, ValueError ):
+                        main.log.exception( "Object not as expected; devices={!r}\nports={!r}".format(
+                            devices[ controller ], 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 ] and "Error" not in hosts[ controller ]:
+                    currentHostsResult = main.Mininet1.compareHosts(
+                            mnHosts,
+                            hosts[ controller ] )
+                elif hosts[ controller ] == []:
+                    currentHostsResult = main.TRUE
+                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" )
+                # CHECKING HOST ATTACHMENT POINTS
+                hostAttachment = True
+                zeroHosts = False
+                # FIXME: topo-HA/obelisk specific mappings:
+                # key is mac and value is dpid
+                mappings = {}
+                for i in range( 1, 29 ):  # hosts 1 through 28
+                    # set up correct variables:
+                    macId = "00:" * 5 + hex( i ).split( "0x" )[1].upper().zfill(2)
+                    if i == 1:
+                        deviceId = "1000".zfill(16)
+                    elif i == 2:
+                        deviceId = "2000".zfill(16)
+                    elif i == 3:
+                        deviceId = "3000".zfill(16)
+                    elif i == 4:
+                        deviceId = "3004".zfill(16)
+                    elif i == 5:
+                        deviceId = "5000".zfill(16)
+                    elif i == 6:
+                        deviceId = "6000".zfill(16)
+                    elif i == 7:
+                        deviceId = "6007".zfill(16)
+                    elif i >= 8 and i <= 17:
+                        dpid = '3' + str( i ).zfill( 3 )
+                        deviceId = dpid.zfill(16)
+                    elif i >= 18 and i <= 27:
+                        dpid = '6' + str( i ).zfill( 3 )
+                        deviceId = dpid.zfill(16)
+                    elif i == 28:
+                        deviceId = "2800".zfill(16)
+                    mappings[ macId ] = deviceId
+                if hosts[ controller ] is not None and "Error" not in hosts[ controller ]:
+                    if hosts[ controller ] == []:
+                        main.log.warn( "There are no hosts discovered" )
+                        zeroHosts = True
+                    else:
+                        for host in hosts[ controller ]:
+                            mac = None
+                            location = None
+                            device = None
+                            port = None
+                            try:
+                                mac = host.get( 'mac' )
+                                assert mac, "mac field could not be found for this host object"
+
+                                location = host.get( 'location' )
+                                assert location, "location field could not be found for this host object"
+
+                                # Trim the protocol identifier off deviceId
+                                device = str( location.get( 'elementId' ) ).split(':')[1]
+                                assert device, "elementId field could not be found for this host location object"
+
+                                port = location.get( 'port' )
+                                assert port, "port field could not be found for this host location object"
+
+                                # Now check if this matches where they should be
+                                if mac and device and port:
+                                    if str( port ) != "1":
+                                        main.log.error( "The attachment port is incorrect for " +
+                                                        "host " + str( mac ) +
+                                                        ". Expected: 1 Actual: " + str( port) )
+                                        hostAttachment = False
+                                    if device != mappings[ str( mac ) ]:
+                                        main.log.error( "The attachment device is incorrect for " +
+                                                        "host " + str( mac ) +
+                                                        ". Expected: " + mappings[ str( mac ) ] +
+                                                        " Actual: " + device )
+                                        hostAttachment = False
+                                else:
+                                    hostAttachment = False
+                            except AssertionError:
+                                main.log.exception( "Json object not as expected" )
+                                main.log.error( repr( host ) )
+                                hostAttachment = False
+                else:
+                    main.log.error( "No hosts json output or \"Error\"" +
+                                    " in output. hosts = " +
+                                    repr( hosts[ controller ] ) )
+                if zeroHosts is False:
+                    # TODO: Find a way to know if there should be hosts in a
+                    #       given point of the test
+                    hostAttachment = True
+
+                # END CHECKING HOST ATTACHMENT POINTS
+                devicesResults = devicesResults and currentDevicesResult
+                linksResults = linksResults and currentLinksResult
+                hostsResults = hostsResults and currentHostsResult
+                hostAttachmentResults = hostAttachmentResults and\
+                                        hostAttachment
+                topoResult = ( devicesResults and linksResults
+                               and hostsResults and ipResult and
+                               hostAttachmentResults )
+        utilities.assert_equals( expect=True,
+                                 actual=topoResult,
+                                 onpass="ONOS topology matches Mininet",
+                                 onfail=topoFailMsg )
+        # End of While loop to pull ONOS state
+
+        # Compare json objects for hosts and dataplane clusters
+
+        # hosts
+        main.step( "Hosts view is consistent across all ONOS nodes" )
+        consistentHostsResult = main.TRUE
+        for controller in range( len( hosts ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if hosts[ controller ] is not None and "Error" not in hosts[ controller ]:
+                if hosts[ controller ] == hosts[ 0 ]:
+                    continue
+                else:  # hosts not consistent
+                    main.log.error( "hosts from ONOS" + controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    main.log.warn( repr( hosts[ controller ] ) )
+                    consistentHostsResult = main.FALSE
+
+            else:
+                main.log.error( "Error in getting ONOS hosts from ONOS" +
+                                 controllerStr )
+                consistentHostsResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " hosts response: " +
+                               repr( hosts[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentHostsResult,
+            onpass="Hosts view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of hosts" )
+
+        main.step( "Hosts information is correct" )
+        hostsResults = hostsResults and ipResult
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Host information is correct",
+            onfail="Host information is incorrect" )
+
+        main.step( "Host attachment points to the network" )
+        utilities.assert_equals(
+            expect=True,
+            actual=hostAttachmentResults,
+            onpass="Hosts are correctly attached to the network",
+            onfail="ONOS did not correctly attach hosts to the network" )
+
+        # Strongly connected clusters of devices
+        main.step( "Clusters view is consistent across all ONOS nodes" )
+        consistentClustersResult = main.TRUE
+        for controller in range( len( clusters ) ):
+            controllerStr = str( main.activeNodes[controller] + 1 )
+            if "Error" not in clusters[ controller ]:
+                if clusters[ controller ] == clusters[ 0 ]:
+                    continue
+                else:  # clusters not consistent
+                    main.log.error( "clusters from ONOS" +
+                                     controllerStr +
+                                     " is inconsistent with ONOS1" )
+                    consistentClustersResult = main.FALSE
+            else:
+                main.log.error( "Error in getting dataplane clusters " +
+                                 "from ONOS" + controllerStr )
+                consistentClustersResult = main.FALSE
+                main.log.warn( "ONOS" + controllerStr +
+                               " clusters response: " +
+                               repr( clusters[ controller ] ) )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=consistentClustersResult,
+            onpass="Clusters view is consistent across all ONOS nodes",
+            onfail="ONOS nodes have different views of clusters" )
+        if not consistentClustersResult:
+            main.log.debug( clusters )
+            for x in links:
+                main.log.warn( "{}: {}".format( len( x ), x ) )
+
+
+        main.step( "There is only one SCC" )
+        # there should always only be one cluster
+        try:
+            numClusters = len( json.loads( clusters[ 0 ] ) )
+        except ( ValueError, TypeError ):
+            main.log.exception( "Error parsing clusters[0]: " +
+                                repr( clusters[0] ) )
+            numClusters = "ERROR"
+        clusterResults = main.FALSE
+        if numClusters == 1:
+            clusterResults = main.TRUE
+        utilities.assert_equals(
+            expect=1,
+            actual=numClusters,
+            onpass="ONOS shows 1 SCC",
+            onfail="ONOS shows " + str( numClusters ) + " SCCs" )
+
+        topoResult = ( devicesResults and linksResults
+                       and hostsResults and consistentHostsResult
+                       and consistentClustersResult and clusterResults
+                       and ipResult and hostAttachmentResults )
+
+        topoResult = topoResult and int( count <= 2 )
+        note = "note it takes about " + str( int( cliTime ) ) + \
+            " seconds for the test to make all the cli calls to fetch " +\
+            "the topology from each ONOS instance"
+        main.log.info(
+            "Very crass estimate for topology discovery/convergence( " +
+            str( note ) + " ): " + str( elapsed ) + " seconds, " +
+            str( count ) + " tries" )
+
+        main.step( "Device information is correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=devicesResults,
+            onpass="Device information is correct",
+            onfail="Device information is incorrect" )
+
+        main.step( "Links are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=linksResults,
+            onpass="Link are correct",
+            onfail="Links are incorrect" )
+
+        main.step( "Hosts are correct" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=hostsResults,
+            onpass="Hosts are correct",
+            onfail="Hosts are incorrect" )
+
+        # FIXME: move this to an ONOS state case
+        main.step( "Checking ONOS nodes" )
+        nodeResults = utilities.retry( main.HA.nodesCheck,
+                                       False,
+                                       args=[main.activeNodes],
+                                       attempts=5 )
+        utilities.assert_equals( expect=True, actual=nodeResults,
+                                 onpass="Nodes check successful",
+                                 onfail="Nodes check NOT successful" )
+        if not nodeResults:
+            for i in main.activeNodes:
+                main.log.debug( "{} components not ACTIVE: \n{}".format(
+                    main.CLIs[i].name,
+                    main.CLIs[i].sendline( "scr:list | grep -v ACTIVE" ) ) )
+
+    def CASE9( self, main ):
+        """
+        Link s3-s28 down
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        # NOTE: You should probably run a topology check after this
+
+        linkSleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
+
+        description = "Turn off a link to ensure that Link Discovery " +\
+                      "is working properly"
+        main.case( description )
+
+        main.step( "Kill Link between s3 and s28" )
+        LinkDown = main.Mininet1.link( END1="s3", END2="s28", OPTION="down" )
+        main.log.info( "Waiting " + str( linkSleep ) +
+                       " seconds for link down to be discovered" )
+        time.sleep( linkSleep )
+        utilities.assert_equals( expect=main.TRUE, actual=LinkDown,
+                                 onpass="Link down successful",
+                                 onfail="Failed to bring link down" )
+        # TODO do some sort of check here
+
+    def CASE10( self, main ):
+        """
+        Link s3-s28 up
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        # NOTE: You should probably run a topology check after this
+
+        linkSleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
+
+        description = "Restore a link to ensure that Link Discovery is " + \
+                      "working properly"
+        main.case( description )
+
+        main.step( "Bring link between s3 and s28 back up" )
+        LinkUp = main.Mininet1.link( END1="s3", END2="s28", OPTION="up" )
+        main.log.info( "Waiting " + str( linkSleep ) +
+                       " seconds for link up to be discovered" )
+        time.sleep( linkSleep )
+        utilities.assert_equals( expect=main.TRUE, actual=LinkUp,
+                                 onpass="Link up successful",
+                                 onfail="Failed to bring link up" )
+        # TODO do some sort of check here
+
+    def CASE11( self, main ):
+        """
+        Switch Down
+        """
+        # NOTE: You should probably run a topology check after this
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        switchSleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
+
+        description = "Killing a switch to ensure it is discovered correctly"
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        main.case( description )
+        switch = main.params[ 'kill' ][ 'switch' ]
+        switchDPID = main.params[ 'kill' ][ 'dpid' ]
+
+        # TODO: Make this switch parameterizable
+        main.step( "Kill " + switch )
+        main.log.info( "Deleting " + switch )
+        main.Mininet1.delSwitch( switch )
+        main.log.info( "Waiting " + str( switchSleep ) +
+                       " seconds for switch down to be discovered" )
+        time.sleep( switchSleep )
+        device = onosCli.getDevice( dpid=switchDPID )
+        # Peek at the deleted switch
+        main.log.warn( str( device ) )
+        result = main.FALSE
+        if device and device[ 'available' ] is False:
+            result = main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=result,
+                                 onpass="Kill switch successful",
+                                 onfail="Failed to kill switch?" )
+
+    def CASE12( self, main ):
+        """
+        Switch Up
+        """
+        # NOTE: You should probably run a topology check after this
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        switchSleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
+        switch = main.params[ 'kill' ][ 'switch' ]
+        switchDPID = main.params[ 'kill' ][ 'dpid' ]
+        links = main.params[ 'kill' ][ 'links' ].split()
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        description = "Adding a switch to ensure it is discovered correctly"
+        main.case( description )
+
+        main.step( "Add back " + switch )
+        main.Mininet1.addSwitch( switch, dpid=switchDPID )
+        for peer in links:
+            main.Mininet1.addLink( switch, peer )
+        ipList = [ node.ip_address for node in main.nodes ]
+        main.Mininet1.assignSwController( sw=switch, ip=ipList )
+        main.log.info( "Waiting " + str( switchSleep ) +
+                       " seconds for switch up to be discovered" )
+        time.sleep( switchSleep )
+        device = onosCli.getDevice( dpid=switchDPID )
+        # Peek at the deleted switch
+        main.log.warn( str( device ) )
+        result = main.FALSE
+        if device and device[ 'available' ]:
+            result = main.TRUE
+        utilities.assert_equals( expect=main.TRUE, actual=result,
+                                 onpass="add switch successful",
+                                 onfail="Failed to add switch?" )
+
+    def CASE13( self, main ):
+        """
+        Clean up
+        """
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case( "Test Cleanup" )
+        main.step( "Killing tcpdumps" )
+        main.Mininet2.stopTcpdump()
+
+        if main.params[ 'BACKUP' ][ 'ENABLED' ] == "True":
+            main.step( "Copying MN pcap and ONOS log files to test station" )
+            # NOTE: MN Pcap file is being saved to logdir.
+            #       We scp this file as MN and TestON aren't necessarily the same vm
+
+            # FIXME: To be replaced with a Jenkin's post script
+            # TODO: Load these from params
+            # NOTE: must end in /
+            logFolder = "/opt/onos/log/"
+            logFiles = [ "karaf.log", "karaf.log.1" ]
+            # NOTE: must end in /
+            for f in logFiles:
+                for node in main.nodes:
+                    dstName = main.logdir + "/" + node.name + "-" + f
+                    main.ONOSbench.secureCopy( node.user_name, node.ip_address,
+                                               logFolder + f, dstName )
+            # std*.log's
+            # NOTE: must end in /
+            logFolder = "/opt/onos/var/"
+            logFiles = [ "stderr.log", "stdout.log" ]
+            # NOTE: must end in /
+            for f in logFiles:
+                for node in main.nodes:
+                    dstName = main.logdir + "/" + node.name + "-" + f
+                    main.ONOSbench.secureCopy( node.user_name, node.ip_address,
+                                               logFolder + f, dstName )
+        else:
+            main.log.debug( "skipping saving log files" )
+
+        main.step( "Stopping Mininet" )
+        mnResult = main.Mininet1.stopNet()
+        utilities.assert_equals( expect=main.TRUE, actual=mnResult,
+                                 onpass="Mininet stopped",
+                                 onfail="MN cleanup NOT successful" )
+
+        main.step( "Checking ONOS Logs for errors" )
+        for node in main.nodes:
+            main.log.debug( "Checking logs for errors on " + node.name + ":" )
+            main.log.warn( main.ONOSbench.checkLogs( node.ip_address ) )
+
+        try:
+            timerLog = open( main.logdir + "/Timers.csv", 'w')
+            main.log.error( ", ".join( labels ) + "\n" + ", ".join( data ) )
+            timerLog.write( ", ".join( labels ) + "\n" + ", ".join( data ) )
+            timerLog.close()
+        except NameError, e:
+            main.log.exception(e)
+
+        main.step( "Stopping webserver" )
+        status = main.Server.stop( )
+        utilities.assert_equals( expect=main.TRUE, actual=status,
+                                 onpass="Stop Server",
+                                 onfail="Failled to stop SimpleHTTPServer" )
+        del main.Server
+
+    def CASE14( self, main ):
+        """
+        start election app on all onos nodes
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        main.case("Start Leadership Election app")
+        main.step( "Install leadership election app" )
+        onosCli = main.CLIs[ main.activeNodes[0] ]
+        appResult = onosCli.activateApp( "org.onosproject.election" )
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=appResult,
+            onpass="Election app installed",
+            onfail="Something went wrong with installing Leadership election" )
+
+        main.step( "Run for election on each node" )
+        for i in main.activeNodes:
+            main.CLIs[i].electionTestRun()
+        time.sleep(5)
+        activeCLIs = [ main.CLIs[i] for i in main.activeNodes ]
+        sameResult, leaders = main.HA.consistentLeaderboards( activeCLIs )
+        utilities.assert_equals(
+            expect=True,
+            actual=sameResult,
+            onpass="All nodes see the same leaderboards",
+            onfail="Inconsistent leaderboards" )
+
+        if sameResult:
+            leader = leaders[ 0 ][ 0 ]
+            if main.nodes[ main.activeNodes[0] ].ip_address in leader:
+                correctLeader = True
+            else:
+                correctLeader = False
+            main.step( "First node was elected leader" )
+            utilities.assert_equals(
+                expect=True,
+                actual=correctLeader,
+                onpass="Correct leader was elected",
+                onfail="Incorrect leader" )
+
+    def CASE15( self, main ):
+        """
+        Check that Leadership Election is still functional
+            15.1 Run election on each node
+            15.2 Check that each node has the same leaders and candidates
+            15.3 Find current leader and withdraw
+            15.4 Check that a new node was elected leader
+            15.5 Check that that new leader was the candidate of old leader
+            15.6 Run for election on old leader
+            15.7 Check that oldLeader is a candidate, and leader if only 1 node
+            15.8 Make sure that the old leader was added to the candidate list
+
+            old and new variable prefixes refer to data from before vs after
+                withdrawl and later before withdrawl vs after re-election
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        description = "Check that Leadership Election is still functional"
+        main.case( description )
+        # NOTE: Need to re-run after restarts since being a canidate is not persistant
+
+        oldLeaders = []  # list of lists of each nodes' candidates before
+        newLeaders = []  # list of lists of each nodes' candidates after
+        oldLeader = ''  # the old leader from oldLeaders, None if not same
+        newLeader = ''  # the new leaders fron newLoeaders, None if not same
+        oldLeaderCLI = None  # the CLI of the old leader used for re-electing
+        expectNoLeader = False  # True when there is only one leader
+        if main.numCtrls == 1:
+            expectNoLeader = True
+
+        main.step( "Run for election on each node" )
+        electionResult = main.TRUE
+
+        for i in main.activeNodes:  # run test election on each node
+            if main.CLIs[i].electionTestRun() == main.FALSE:
+                electionResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=electionResult,
+            onpass="All nodes successfully ran for leadership",
+            onfail="At least one node failed to run for leadership" )
+
+        if electionResult == main.FALSE:
+            main.log.error(
+                "Skipping Test Case because Election Test App isn't loaded" )
+            main.skipCase()
+
+        main.step( "Check that each node shows the same leader and candidates" )
+        failMessage = "Nodes have different leaderboards"
+        activeCLIs = [ main.CLIs[i] for i in main.activeNodes ]
+        sameResult, oldLeaders = main.HA.consistentLeaderboards( activeCLIs )
+        if sameResult:
+            oldLeader = oldLeaders[ 0 ][ 0 ]
+            main.log.warn( oldLeader )
+        else:
+            oldLeader = None
+        utilities.assert_equals(
+            expect=True,
+            actual=sameResult,
+            onpass="Leaderboards are consistent for the election topic",
+            onfail=failMessage )
+
+        main.step( "Find current leader and withdraw" )
+        withdrawResult = main.TRUE
+        # do some sanity checking on leader before using it
+        if oldLeader is None:
+            main.log.error( "Leadership isn't consistent." )
+            withdrawResult = main.FALSE
+        # Get the CLI of the oldLeader
+        for i in main.activeNodes:
+            if oldLeader == main.nodes[ i ].ip_address:
+                oldLeaderCLI = main.CLIs[ i ]
+                break
+        else:  # FOR/ELSE statement
+            main.log.error( "Leader election, could not find current leader" )
+        if oldLeader:
+            withdrawResult = oldLeaderCLI.electionTestWithdraw()
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=withdrawResult,
+            onpass="Node was withdrawn from election",
+            onfail="Node was not withdrawn from election" )
+
+        main.step( "Check that a new node was elected leader" )
+        failMessage = "Nodes have different leaders"
+        # Get new leaders and candidates
+        newLeaderResult, newLeaders = main.HA.consistentLeaderboards( activeCLIs )
+        newLeader = None
+        if newLeaderResult:
+            if newLeaders[ 0 ][ 0 ] == 'none':
+                main.log.error( "No leader was elected on at least 1 node" )
+                if not expectNoLeader:
+                    newLeaderResult = False
+            newLeader = newLeaders[ 0 ][ 0 ]
+
+        # Check that the new leader is not the older leader, which was withdrawn
+        if newLeader == oldLeader:
+            newLeaderResult = False
+            main.log.error( "All nodes still see old leader: " + str( oldLeader ) +
+                    " as the current leader" )
+        utilities.assert_equals(
+            expect=True,
+            actual=newLeaderResult,
+            onpass="Leadership election passed",
+            onfail="Something went wrong with Leadership election" )
+
+        main.step( "Check that that new leader was the candidate of old leader" )
+        # candidates[ 2 ] should become the top candidate after withdrawl
+        correctCandidateResult = main.TRUE
+        if expectNoLeader:
+            if newLeader == 'none':
+                main.log.info( "No leader expected. None found. Pass" )
+                correctCandidateResult = main.TRUE
+            else:
+                main.log.info( "Expected no leader, got: " + str( newLeader ) )
+                correctCandidateResult = main.FALSE
+        elif len( oldLeaders[0] ) >= 3:
+            if newLeader == oldLeaders[ 0 ][ 2 ]:
+                # correct leader was elected
+                correctCandidateResult = main.TRUE
+            else:
+                correctCandidateResult = main.FALSE
+                main.log.error( "Candidate {} was elected. {} should have had priority.".format(
+                                    newLeader, oldLeaders[ 0 ][ 2 ] ) )
+        else:
+            main.log.warn( "Could not determine who should be the correct leader" )
+            main.log.debug( oldLeaders[ 0 ] )
+            correctCandidateResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=correctCandidateResult,
+            onpass="Correct Candidate Elected",
+            onfail="Incorrect Candidate Elected" )
+
+        main.step( "Run for election on old leader( just so everyone " +
+                   "is in the hat )" )
+        if oldLeaderCLI is not None:
+            runResult = oldLeaderCLI.electionTestRun()
+        else:
+            main.log.error( "No old leader to re-elect" )
+            runResult = main.FALSE
+        utilities.assert_equals(
+            expect=main.TRUE,
+            actual=runResult,
+            onpass="App re-ran for election",
+            onfail="App failed to run for election" )
+
+        main.step(
+            "Check that oldLeader is a candidate, and leader if only 1 node" )
+        # verify leader didn't just change
+        # Get new leaders and candidates
+        reRunLeaders = []
+        time.sleep( 5 )  # Paremterize
+        positionResult, reRunLeaders = main.HA.consistentLeaderboards( activeCLIs )
+
+        # Check that the re-elected node is last on the candidate List
+        if not reRunLeaders[0]:
+            positionResult = main.FALSE
+        elif oldLeader != reRunLeaders[ 0 ][ -1 ]:
+            main.log.error( "Old Leader ({}) not in the proper position: {} ".format( str( oldLeader),
+                                                                                      str( reRunLeaders[ 0 ] ) ) )
+            positionResult = main.FALSE
+        utilities.assert_equals(
+            expect=True,
+            actual=positionResult,
+            onpass="Old leader successfully re-ran for election",
+            onfail="Something went wrong with Leadership election after " +
+                   "the old leader re-ran for election" )
+
+    def CASE16( self, main ):
+        """
+        Install Distributed Primitives app
+        """
+        import time
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+
+        # Variables for the distributed primitives tests
+        global pCounterName
+        global pCounterValue
+        global onosSet
+        global onosSetName
+        pCounterName = "TestON-Partitions"
+        pCounterValue = 0
+        onosSet = set([])
+        onosSetName = "TestON-set"
+
+        description = "Install Primitives app"
+        main.case( description )
+        main.step( "Install Primitives app" )
+        appName = "org.onosproject.distributedprimitives"
+        node = main.activeNodes[0]
+        appResults = main.CLIs[node].activateApp( appName )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=appResults,
+                                 onpass="Primitives app activated",
+                                 onfail="Primitives app not activated" )
+        time.sleep( 5 )  # To allow all nodes to activate
+
+    def CASE17( self, main ):
+        """
+        Check for basic functionality with distributed primitives
+        """
+        # Make sure variables are defined/set
+        assert main.numCtrls, "main.numCtrls not defined"
+        assert main, "main not defined"
+        assert utilities.assert_equals, "utilities.assert_equals not defined"
+        assert main.CLIs, "main.CLIs not defined"
+        assert main.nodes, "main.nodes not defined"
+        assert pCounterName, "pCounterName not defined"
+        assert onosSetName, "onosSetName not defined"
+        # NOTE: assert fails if value is 0/None/Empty/False
+        try:
+            pCounterValue
+        except NameError:
+            main.log.error( "pCounterValue not defined, setting to 0" )
+            pCounterValue = 0
+        try:
+            onosSet
+        except NameError:
+            main.log.error( "onosSet not defined, setting to empty Set" )
+            onosSet = set([])
+        # Variables for the distributed primitives tests. These are local only
+        addValue = "a"
+        addAllValue = "a b c d e f"
+        retainValue = "c d e f"
+
+        description = "Check for basic functionality with distributed " +\
+                      "primitives"
+        main.case( description )
+        main.caseExplanation = "Test the methods of the distributed " +\
+                                "primitives (counters and sets) throught the cli"
+        # DISTRIBUTED ATOMIC COUNTERS
+        # Partitioned counters
+        main.step( "Increment then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterAddAndGet-" + str( i ),
+                             args=[ pCounterName ] )
+            pCounterValue += 1
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Get then Increment a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestGetAndAdd,
+                             name="counterGetAndAdd-" + str( i ),
+                             args=[ pCounterName ] )
+            addedPValues.append( pCounterValue )
+            pCounterValue += 1
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Counters we added have the correct values" )
+        incrementCheck = main.HA.counterCheck( pCounterName, pCounterValue )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=incrementCheck,
+                                 onpass="Added counters are correct",
+                                 onfail="Added counters are incorrect" )
+
+        main.step( "Add -8 to then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": -8 } )
+            pCounterValue += -8
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Add 5 to then get a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestAddAndGet,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": 5 } )
+            pCounterValue += 5
+            addedPValues.append( pCounterValue )
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Get then add 5 to a default counter on each node" )
+        pCounters = []
+        threads = []
+        addedPValues = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].counterTestGetAndAdd,
+                             name="counterIncrement-" + str( i ),
+                             args=[ pCounterName ],
+                             kwargs={ "delta": 5 } )
+            addedPValues.append( pCounterValue )
+            pCounterValue += 5
+            threads.append( t )
+            t.start()
+
+        for t in threads:
+            t.join()
+            pCounters.append( t.result )
+        # Check that counter incremented numController times
+        pCounterResults = True
+        for i in addedPValues:
+            tmpResult = i in pCounters
+            pCounterResults = pCounterResults and tmpResult
+            if not tmpResult:
+                main.log.error( str( i ) + " is not in partitioned "
+                                "counter incremented results" )
+        utilities.assert_equals( expect=True,
+                                 actual=pCounterResults,
+                                 onpass="Default counter incremented",
+                                 onfail="Error incrementing default" +
+                                        " counter" )
+
+        main.step( "Counters we added have the correct values" )
+        incrementCheck = main.HA.counterCheck( pCounterName, pCounterValue )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=incrementCheck,
+                                 onpass="Added counters are correct",
+                                 onfail="Added counters are incorrect" )
+
+        # DISTRIBUTED SETS
+        main.step( "Distributed Set get" )
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=getResults,
+                                 onpass="Set elements are correct",
+                                 onfail="Set elements are incorrect" )
+
+        main.step( "Distributed Set size" )
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=sizeResults,
+                                 onpass="Set sizes are correct",
+                                 onfail="Set sizes are incorrect" )
+
+        main.step( "Distributed Set add()" )
+        onosSet.add( addValue )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAdd-" + str( i ),
+                             args=[ onosSetName, addValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addResults = main.FALSE
+            else:
+                # unexpected result
+                addResults = main.FALSE
+        if addResults != main.TRUE:
+            main.log.error( "Error executing set add" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node + " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node + " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addResults = addResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addResults,
+                                 onpass="Set add correct",
+                                 onfail="Set add was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set contains()" )
+        containsResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setContains-" + str( i ),
+                             args=[ onosSetName ],
+                             kwargs={ "values": addValue } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            # NOTE: This is the tuple
+            containsResponses.append( t.result )
+
+        containsResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if containsResponses[ i ] == main.ERROR:
+                containsResults = main.FALSE
+            else:
+                containsResults = containsResults and\
+                                  containsResponses[ i ][ 1 ]
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=containsResults,
+                                 onpass="Set contains is functional",
+                                 onfail="Set contains failed" )
+
+        main.step( "Distributed Set containsAll()" )
+        containsAllResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setContainsAll-" + str( i ),
+                             args=[ onosSetName ],
+                             kwargs={ "values": addAllValue } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            # NOTE: This is the tuple
+            containsAllResponses.append( t.result )
+
+        containsAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if containsResponses[ i ] == main.ERROR:
+                containsResults = main.FALSE
+            else:
+                containsResults = containsResults and\
+                                  containsResponses[ i ][ 1 ]
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=containsAllResults,
+                                 onpass="Set containsAll is functional",
+                                 onfail="Set containsAll failed" )
+
+        main.step( "Distributed Set remove()" )
+        onosSet.remove( addValue )
+        removeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestRemove-" + str( i ),
+                             args=[ onosSetName, addValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            removeResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        removeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if removeResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif removeResponses[ i ] == main.FALSE:
+                # not in set, probably fine
+                pass
+            elif removeResponses[ i ] == main.ERROR:
+                # Error in execution
+                removeResults = main.FALSE
+            else:
+                # unexpected result
+                removeResults = main.FALSE
+        if removeResults != main.TRUE:
+            main.log.error( "Error executing set remove" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        removeResults = removeResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeResults,
+                                 onpass="Set remove correct",
+                                 onfail="Set remove was incorrect" )
+
+        main.step( "Distributed Set removeAll()" )
+        onosSet.difference_update( addAllValue.split() )
+        removeAllResponses = []
+        threads = []
+        try:
+            for i in main.activeNodes:
+                t = main.Thread( target=main.CLIs[i].setTestRemove,
+                                 name="setTestRemoveAll-" + str( i ),
+                                 args=[ onosSetName, addAllValue ] )
+                threads.append( t )
+                t.start()
+            for t in threads:
+                t.join()
+                removeAllResponses.append( t.result )
+        except Exception, e:
+            main.log.exception(e)
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        removeAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if removeAllResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif removeAllResponses[ i ] == main.FALSE:
+                # not in set, probably fine
+                pass
+            elif removeAllResponses[ i ] == main.ERROR:
+                # Error in execution
+                removeAllResults = main.FALSE
+            else:
+                # unexpected result
+                removeAllResults = main.FALSE
+        if removeAllResults != main.TRUE:
+            main.log.error( "Error executing set removeAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        removeAllResults = removeAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeAllResults,
+                                 onpass="Set removeAll correct",
+                                 onfail="Set removeAll was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set clear()" )
+        onosSet.clear()
+        clearResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestClear-" + str( i ),
+                             args=[ onosSetName, " "],  # Values doesn't matter
+                             kwargs={ "clear": True } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            clearResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        clearResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if clearResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif clearResponses[ i ] == main.FALSE:
+                # Nothing set, probably fine
+                pass
+            elif clearResponses[ i ] == main.ERROR:
+                # Error in execution
+                clearResults = main.FALSE
+            else:
+                # unexpected result
+                clearResults = main.FALSE
+        if clearResults != main.TRUE:
+            main.log.error( "Error executing set clear" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        clearResults = clearResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=clearResults,
+                                 onpass="Set clear correct",
+                                 onfail="Set clear was incorrect" )
+
+        main.step( "Distributed Set addAll()" )
+        onosSet.update( addAllValue.split() )
+        addResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestAdd,
+                             name="setTestAddAll-" + str( i ),
+                             args=[ onosSetName, addAllValue ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            addResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        addAllResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if addResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif addResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif addResponses[ i ] == main.ERROR:
+                # Error in execution
+                addAllResults = main.FALSE
+            else:
+                # unexpected result
+                addAllResults = main.FALSE
+        if addAllResults != main.TRUE:
+            main.log.error( "Error executing set addAll" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node +
+                                " expected a size of " + str( size ) +
+                                " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        addAllResults = addAllResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=addAllResults,
+                                 onpass="Set addAll correct",
+                                 onfail="Set addAll was incorrect" )
+
+        main.step( "Distributed Set retain()" )
+        onosSet.intersection_update( retainValue.split() )
+        retainResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestRemove,
+                             name="setTestRetain-" + str( i ),
+                             args=[ onosSetName, retainValue ],
+                             kwargs={ "retain": True } )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            retainResponses.append( t.result )
+
+        # main.TRUE = successfully changed the set
+        # main.FALSE = action resulted in no change in set
+        # main.ERROR - Some error in executing the function
+        retainResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            if retainResponses[ i ] == main.TRUE:
+                # All is well
+                pass
+            elif retainResponses[ i ] == main.FALSE:
+                # Already in set, probably fine
+                pass
+            elif retainResponses[ i ] == main.ERROR:
+                # Error in execution
+                retainResults = main.FALSE
+            else:
+                # unexpected result
+                retainResults = main.FALSE
+        if retainResults != main.TRUE:
+            main.log.error( "Error executing set retain" )
+
+        # Check if set is still correct
+        size = len( onosSet )
+        getResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestGet,
+                             name="setTestGet-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            getResponses.append( t.result )
+        getResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if isinstance( getResponses[ i ], list):
+                current = set( getResponses[ i ] )
+                if len( current ) == len( getResponses[ i ] ):
+                    # no repeats
+                    if onosSet != current:
+                        main.log.error( "ONOS" + node +
+                                        " has incorrect view" +
+                                        " of set " + onosSetName + ":\n" +
+                                        str( getResponses[ i ] ) )
+                        main.log.debug( "Expected: " + str( onosSet ) )
+                        main.log.debug( "Actual: " + str( current ) )
+                        getResults = main.FALSE
+                else:
+                    # error, set is not a set
+                    main.log.error( "ONOS" + node +
+                                    " has repeat elements in" +
+                                    " set " + onosSetName + ":\n" +
+                                    str( getResponses[ i ] ) )
+                    getResults = main.FALSE
+            elif getResponses[ i ] == main.ERROR:
+                getResults = main.FALSE
+        sizeResponses = []
+        threads = []
+        for i in main.activeNodes:
+            t = main.Thread( target=main.CLIs[i].setTestSize,
+                             name="setTestSize-" + str( i ),
+                             args=[ onosSetName ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            sizeResponses.append( t.result )
+        sizeResults = main.TRUE
+        for i in range( len( main.activeNodes ) ):
+            node = str( main.activeNodes[i] + 1 )
+            if size != sizeResponses[ i ]:
+                sizeResults = main.FALSE
+                main.log.error( "ONOS" + node + " expected a size of " +
+                                str( size ) + " for set " + onosSetName +
+                                " but got " + str( sizeResponses[ i ] ) )
+        retainResults = retainResults and getResults and sizeResults
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=retainResults,
+                                 onpass="Set retain correct",
+                                 onfail="Set retain was incorrect" )
+
+        # Transactional maps
+        main.step( "Partitioned Transactional maps put" )
+        tMapValue = "Testing"
+        numKeys = 100
+        putResult = True
+        node = main.activeNodes[0]
+        putResponses = main.CLIs[node].transactionalMapPut( numKeys, tMapValue )
+        if putResponses and len( putResponses ) == 100:
+            for i in putResponses:
+                if putResponses[ i ][ 'value' ] != tMapValue:
+                    putResult = False
+        else:
+            putResult = False
+        if not putResult:
+            main.log.debug( "Put response values: " + str( putResponses ) )
+        utilities.assert_equals( expect=True,
+                                 actual=putResult,
+                                 onpass="Partitioned Transactional Map put successful",
+                                 onfail="Partitioned Transactional Map put values are incorrect" )
+
+        main.step( "Partitioned Transactional maps get" )
+        getCheck = True
+        for n in range( 1, numKeys + 1 ):
+            getResponses = []
+            threads = []
+            valueCheck = True
+            for i in main.activeNodes:
+                t = main.Thread( target=main.CLIs[i].transactionalMapGet,
+                                 name="TMap-get-" + str( i ),
+                                 args=[ "Key" + str( n ) ] )
+                threads.append( t )
+                t.start()
+            for t in threads:
+                t.join()
+                getResponses.append( t.result )
+            for node in getResponses:
+                if node != tMapValue:
+                    valueCheck = False
+            if not valueCheck:
+                main.log.warn( "Values for key 'Key" + str( n ) + "' do not match:" )
+                main.log.warn( getResponses )
+            getCheck = getCheck and valueCheck
+        utilities.assert_equals( expect=True,
+                                 actual=getCheck,
+                                 onpass="Partitioned Transactional Map get values were correct",
+                                 onfail="Partitioned Transactional Map values incorrect" )
diff --git a/TestON/tests/HA/HAswapNodes/HAswapNodes.topo b/TestON/tests/HA/HAswapNodes/HAswapNodes.topo
new file mode 100644
index 0000000..81cf47a
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/HAswapNodes.topo
@@ -0,0 +1,171 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOScli1>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli1>
+
+        <ONOScli2>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli2>
+
+        <ONOScli3>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli3>
+
+
+        <ONOScli4>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli4>
+
+
+        <ONOScli5>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli5>
+
+
+        <ONOScli6>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>7</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli6>
+
+
+        <ONOScli7>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>8</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli7>
+
+        <ONOS1>
+            <host>OC1</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>9</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS1>
+
+        <ONOS2>
+            <host>OC2</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>10</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS2>
+
+        <ONOS3>
+            <host>OC3</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>11</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS3>
+
+        <ONOS4>
+            <host>OC4</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>12</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS4>
+
+        <ONOS5>
+            <host>OC5</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>13</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS5>
+
+        <ONOS6>
+            <host>OC6</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>14</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS6>
+
+        <ONOS7>
+            <host>OC7</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>15</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS7>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>16</connect_order>
+            <COMPONENTS>
+                #Specify the Option for mininet
+                <arg1> --custom ~/mininet/custom/obelisk.py </arg1>
+                <arg2> --topo obelisk </arg2>
+                <arg3> --switch ovs,protocols=OpenFlow13 </arg3>
+                <controller> none </controller>
+                <home>~/mininet/custom/</home>
+            </COMPONENTS>
+        </Mininet1>
+
+        <Mininet2>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>RemoteMininetDriver</type>
+            <connect_order>17</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </Mininet2>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/HA/HAswapNodes/README b/TestON/tests/HA/HAswapNodes/README
new file mode 100644
index 0000000..281b7c2
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/README
@@ -0,0 +1,20 @@
+This test is designed to verify that an ONOS cluster behaves correctly when
+nodes are added or removed from a cluster dynamically via modifying a remote
+cluster metadata file.
+
+
+The gerneral structure for the test:
+- Startup
+- Assign switches
+- Verify ONOS state and functionality
+    - Device mastership
+    - Intents
+    - Leadership election
+    - Distributed Primitives
+- Scale ONOS cluster size from 1 to 7 to 1 by increments of 2
+    - Modify cluster metadata file
+    - Start or stop ONOS nodes
+    - Verify ONOS state and functionality
+    - Dataplane failures
+        - link down and up
+        - switch down and up
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/HA/HAswapNodes/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/HA/HAswapNodes/__init__.py
diff --git a/TestON/tests/HA/HAswapNodes/dependencies/Server.py b/TestON/tests/HA/HAswapNodes/dependencies/Server.py
new file mode 100644
index 0000000..bff976f
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/dependencies/Server.py
@@ -0,0 +1,148 @@
+"Functions for using the SimpleHTTPServer python module"
+import re
+
+class Server():
+
+    def __init__( self ):
+        self.default = ''
+        self.PID = -1
+        self.component = None
+        self.rootDir = None
+
+    def __del__( self ):
+        self.stop()
+
+    def start( self, component, rootDir, port=8000, logDir=None ):
+        """
+        Start SimpleHTTPServer as a background process from rootDir on the
+        given component. The webserver will listen on port and if specified,
+        output will be redirected to logDir.
+
+        Arguments:
+        - component = The TestON component handle to start the webserver on
+        - rootDir = The root directory for the web content
+        - port = The port number for the webserver to listen on. Defaults to 8000
+        - logDir = If specified, the output of the webserver will be redirected
+                   to this file. Note that this should be either an absolute path
+                   or relative to rootDir.
+        Returns:
+            main.TRUE if the command succedes or main.FALSE if there is an error.
+        """
+        retValue = main.TRUE
+        self.rootDir = rootDir
+        try:
+            # Save component for this instance so other functions can use it
+            self.component = component
+            main.log.info( "Starting SimpleHTTPServer on " + component.name )
+            if component.handle:
+                handle = component.handle
+                # cd to rootDir
+                handle.sendline( "cd " + str( rootDir ) )
+                handle.expect( "\$" )
+                # Start server
+                cmd = "python -m SimpleHTTPServer {}".format( port )
+                if logDir:
+                    cmd += " &> {}".format( logDir )  # pipe all output to a file
+                else:
+                    cmd += "&> {dev/null}" # Throw away all output
+                cmd += " &"
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                response = handle.before
+                # Return to home dir
+                handle.sendline( "cd " + component.home )
+                handle.expect( "\$" )
+                response += handle.before
+                if "Exit" in response:
+                    main.log.error( "Error starting server. Check server log for details" )
+                    main.log.debug( handle.before )
+                    retValue = main.FALSE
+                # capture PID for later use
+                # EX: [1] 67987
+                match = re.search( "\[\d\] (?P<PID>\d+)", response )
+                if match:
+                    self.PID = match.group( "PID" )
+                else:
+                    main.log.warn( "Could not find PID" )
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error starting web server" )
+            retValue = main.FALSE
+        return retValue
+
+    def stop( self ):
+        """
+        Kills the process of the server. Note that this function must be run
+        from the same instance of the server class that the server was started
+        on.
+        """
+        retValue = main.TRUE
+        try:
+            main.log.info( "Stopping Server." )
+            assert self.component, "Component not specified"
+            assert self.PID, "PID not found"
+            if self.component.handle:
+                handle = self.component.handle
+                cmd = "sudo kill {}".format( self.PID )
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                # TODO: What is bad output? cannot sudo?
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error stopping web server" )
+            retValue = main.FALSE
+        return retValue
+
+    def generateFile( self, nodes, equal=False, filename="cluster.json" ):
+        """
+        Generate custom metadata file in the root directory using the custom
+        onos-gen-partitions file which should also be located in the root
+        directory.
+
+        Note that this function needs to be run after the start function has
+        been called for this instance.
+
+        Arguments:
+        - nodes = The number of ONOS nodes to include in the cluster. Will
+                  include nodes in ascending order, I.E. OC1, OC2, etc
+
+        Optional Arguments:
+        - equal = Specifies whether all nodes should participate in every
+                  partition. Defaults to False.
+        - filename = The name of the file to save the cluster metadata to.
+                     Defaults to "cluster.json".
+        Returns:
+            main.TRUE if the command succedes or main.FALSE if there is an error.
+        """
+        retValue = main.TRUE
+        try:
+            if self.component.handle:
+                assert self.component, "Component not specified. Please start the server first"
+                assert self.rootDir, "Root directory not found"
+                handle = self.component.handle
+                # cd to rootDir
+                handle.sendline( "cd " + str( self.rootDir ) )
+                handle.expect( "\$" )
+                cmd = "./onos-gen-partitions {} {} ".format( filename, nodes )
+                if equal:
+                    cmd += "-e"
+                handle.sendline( cmd )
+                handle.expect( "\$" )
+                response = handle.before
+                # Return to home dir
+                handle.sendline( "cd " + self.component.home )
+                handle.expect( "\$" )
+                response += handle.before
+                if "Traceback" in response:
+                    main.log.error( handle.before )
+                    retValue = main.FALSE
+            else:
+                main.log.error( "Component handle is not set" )
+                retValue = main.FALSE
+        except Exception:
+            main.log.exception( "Error generating metadata file" )
+        return retValue
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/HA/HAswapNodes/dependencies/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/HA/HAswapNodes/dependencies/__init__.py
diff --git a/TestON/tests/HA/HAswapNodes/dependencies/onos-gen-partitions b/TestON/tests/HA/HAswapNodes/dependencies/onos-gen-partitions
new file mode 100755
index 0000000..ae7da6c
--- /dev/null
+++ b/TestON/tests/HA/HAswapNodes/dependencies/onos-gen-partitions
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+"""
+  Generate the partitions json file from the $OC* environment variables
+
+  Usage: onos-gen-partitions output_file [num_nodes] [-e]
+  If output file is not provided, the json is written to stdout.
+"""
+
+from os import environ
+from collections import deque, OrderedDict
+import re
+import json
+import sys
+import hashlib
+
+convert = lambda text: int(text) if text.isdigit() else text.lower()
+alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
+
+def get_OC_vars():
+  vars = []
+  for var in environ:
+    if re.match(r"OC[0-9]+", var):
+      vars.append(var)
+  return sorted(vars, key=alphanum_key)
+
+def get_nodes(vars, port=9876):
+  node = lambda k: { 'id': k, 'ip': k, 'port': port }
+  return [ node(environ[v]) for v in vars ]
+
+def generate_base_partition(nodes):
+  return {
+            'id': 0,
+            'members': nodes
+         }
+
+def generate_extended_partitions_scaling(nodes, k, partitions=3, equal=False):
+  l = deque(nodes)
+  perms = []
+  for i in range(1, partitions + 1):
+    if equal:
+      members = list(l)
+    else:
+      members = list(l)[:k]
+
+    part = {
+             'id': i,
+             'members': members
+           }
+    perms.append(part)
+    l.rotate(-2)
+  return perms
+
+if __name__ == '__main__':
+  vars = get_OC_vars()
+  # NOTE: likely prone to errors
+  nodes = get_nodes(vars)
+  num = None
+  equal = False
+  if len(sys.argv) >= 3:
+      num = int(sys.argv[2])
+      try:
+          equal = "-e" in sys.argv[3]
+      except:
+          equal = False
+  if num:
+      nodes = nodes[:num]
+
+  base_partition = generate_base_partition([v.get('id') for v in nodes])
+  extended_partitions = generate_extended_partitions_scaling([v.get('id') for v in nodes],
+          3, equal=equal)
+  partitions = []
+  partitions.append(base_partition)
+  partitions.extend(extended_partitions)
+  name = hash("HAScaling")
+  data = {
+           'name': name,
+           'nodes': nodes,
+           'partitions': partitions
+         }
+  output = json.dumps(data, indent=4)
+
+  if len(sys.argv) >= 2:
+    filename = sys.argv[1]
+    with open(filename, 'w') as f:
+      f.write(output)
+  else:
+    print output
diff --git a/TestON/tests/HA/dependencies/onos-gen-partitions b/TestON/tests/HA/dependencies/onos-gen-partitions
index 06c5ec9..b83aea6 100755
--- a/TestON/tests/HA/dependencies/onos-gen-partitions
+++ b/TestON/tests/HA/dependencies/onos-gen-partitions
@@ -27,12 +27,6 @@
   node = lambda k: { 'id': k, 'ip': k, 'port': port }
   return [ node(environ[v]) for v in vars ]
 
-def generate_base_partition(nodes):
-  return {
-            'id': 0,
-            'members': nodes
-         }
-
 def generate_extended_partitions(nodes, k):
   l = deque(nodes)
   perms = []
@@ -60,10 +54,8 @@
 if __name__ == '__main__':
   vars = get_OC_vars()
   nodes = get_nodes(vars)
-  base_partition = generate_base_partition([v.get('id') for v in nodes])
   extended_partitions = generate_extended_partitions_HA([v.get('id') for v in nodes], 3)
   partitions = []
-  partitions.append(base_partition)
   partitions.extend(extended_partitions)
   name = 0
   for node in nodes:
diff --git a/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.py b/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.py
index ca5e240..b767835 100755
--- a/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.py
+++ b/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.py
@@ -184,8 +184,8 @@
         cliResult = main.TRUE
         for i in range( i, main.numCtrls ):
             cliResult = cliResult and \
-                        main.CLIs[ i ].startOnosCli( ONOSIp=main.ONOSip[ i ] )
-            main.log.info("ONOSip is: " + main.ONOSip[i])
+                        main.ONOScli1.startCellCli( )
+            main.log.info("ONOSip is: " + main.ONOScli1.ip_address)
         stepResult = cliResult
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -204,8 +204,7 @@
 
         main.step("Activate openflow-base App")
         app = main.params['CASE10']['app']
-        stepResult = main.ONOSbench.onosCli( ONOSIp = main.ONOSip[0],
-                                             cmdstr = "app activate " + app )
+        stepResult = main.ONOScli1.activateApp( app )
         time.sleep(main.cfgSleep)
         main.log.info(stepResult)
         utilities.assert_equals( expect=main.TRUE,
@@ -216,9 +215,9 @@
         time.sleep(main.cfgSleep)
 
 
-        main.step( "Disable AdaptiveFlowSampling ")
-        stepResult = main.ONOSbench.onosCfgSet( main.ONOSip[0], "org.onosproject.provider.of.flow.impl.OpenFlowRuleProvider",
-                                   "adaptiveFlowSampling " + main.params['CASE10']['adaptiveFlowenabled'])
+        main.step( "Configure AdaptiveFlowSampling ")
+        stepResult = main.ONOScli1.setCfg( component = "org.onosproject.provider.of.flow.impl.OpenFlowRuleProvider",
+                                   propName = "adaptiveFlowSampling ",  value = main.params['CASE10']['adaptiveFlowenabled'])
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="App Configuration Succeeded! ",
diff --git a/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.topo b/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.topo
index 0e3543e..b37a17a 100755
--- a/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.topo
+++ b/TestON/tests/MISC/SCPFbatchFlowResp/SCPFbatchFlowResp.topo
@@ -3,19 +3,18 @@
 
         <ONOSbench>
             <host>localhost</host>
-            <user>admin</user>
-            <password>onos_test</password>
+            <user>sdn</user>
+            <password>rocks</password>
             <type>OnosDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/Projects/onos</home>
             </COMPONENTS>
         </ONOSbench>
 
         <ONOScli1>
-            <host>localhost</host>
-            <user>admin</user>
-            <password>onos_test</password>
+            <host>OC1</host>
+            <user>sdn</user>
+            <password>rocks</password>
             <type>OnosCliDriver</type>
             <connect_order>2</connect_order>
             <COMPONENTS>
@@ -24,8 +23,8 @@
 
         <Mininet1>
             <host>localhost</host>
-            <user>admin</user>
-            <password>onos_test</password>
+            <user>sdn</user>
+            <password>rocks</password>
             <type>MininetCliDriver</type>
             <connect_order>5</connect_order>
             <COMPONENTS> </COMPONENTS>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/MISC/__init__.py
old mode 100644
new mode 100755
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/MISC/__init__.py
diff --git a/TestON/tests/PLAT/PLATdockertest/PLATdockertest.py b/TestON/tests/PLAT/PLATdockertest/PLATdockertest.py
index 6e4acda..7133931 100755
--- a/TestON/tests/PLAT/PLATdockertest/PLATdockertest.py
+++ b/TestON/tests/PLAT/PLATdockertest/PLATdockertest.py
@@ -163,7 +163,7 @@
         main.ONOSbenchDocker.onosFormCluster(cmdPath = clcmdpath, onosIPs=IPlist, user=dkruser, passwd = dkrpasswd)
         main.log.info("Wait for cluster to form with sleep time of " + str(startupSleep))
         time.sleep(startupSleep)
-        status, response = main.ONOSbenchRest.send(ip=IPlist[0],port=8181, url="/cluster")
+        status, response = main.ONOSbenchRest.send(ip=IPlist[0], port=8181, url="/cluster")
         main.log.debug("Rest call response: " + str(status) + " - " + response)
         if status == 200:
             jrsp = json.loads(response)
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/Dependency/startUp.py b/TestON/tests/SAMP/SAMPstartTemplate/Dependency/startUp.py
deleted file mode 100644
index bf2a2b6..0000000
--- a/TestON/tests/SAMP/SAMPstartTemplate/Dependency/startUp.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-    This wrapper function is use for starting up onos instance
-"""
-
-import time
-import os
-import json
-
-def onosBuild( main, gitBranch ):
-    """
-        This includes pulling ONOS and building it using maven install
-    """
-
-    buildResult = main.FALSE
-
-    # Git checkout a branch of ONOS
-    checkOutResult = main.ONOSbench.gitCheckout( gitBranch )
-    # Does the git pull on the branch that was checked out
-    if not checkOutResult:
-        main.log.warn( "Failed to checked out " + gitBranch +
-                                           " branch")
-    else:
-        main.log.info( "Successfully checked out " + gitBranch +
-                                           " branch")
-    gitPullResult = main.ONOSbench.gitPull()
-    if gitPullResult == main.ERROR:
-        main.log.error( "Error pulling git branch" )
-    else:
-        main.log.info( "Successfully pulled " + gitBranch + " branch" )
-
-    # Maven clean install
-    buildResult = main.ONOSbench.cleanInstall()
-
-    return buildResult
-
-
-
-
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/README b/TestON/tests/SAMP/SAMPstartTemplate/README
deleted file mode 100644
index 359943e..0000000
--- a/TestON/tests/SAMP/SAMPstartTemplate/README
+++ /dev/null
@@ -1,5 +0,0 @@
-Summary:
-        This is a Sample test suite that demonstrates starting up ONOS
-        and scalling to multiple instances. It also has extra
-        functionalilty that allows the tester to bypass the ONOS
-        package and install case (case 2) to save time.
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.params b/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.params
deleted file mode 100755
index 9120c6a..0000000
--- a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.params
+++ /dev/null
@@ -1,34 +0,0 @@
-<PARAMS>
-
-    <testcases>1,2,2,2</testcases>
-
-    <SCALE>
-        <size>1,2,3</size>
-        <max>3</max>
-    </SCALE>
-
-    <DEPENDENCY>
-        <path>/tests/SAMPstartTemplate/dependencies/</path>
-        <wrapper1>startUp</wrapper1>
-        <topology>newFuncTopo.py</topology>
-    </DEPENDENCY>
-
-    <ENV>
-        <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,proxyarp,mobility</cellApps>
-    </ENV>
-
-    <GIT>
-        <pull>False</pull>
-        <branch>master</branch>
-    </GIT>
-
-    <CTRL>
-        <port>6653</port>
-    </CTRL>
-
-    <SLEEP>
-        <startup>15</startup>
-    </SLEEP>
-
-</PARAMS>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.py b/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.py
deleted file mode 100644
index b6184da..0000000
--- a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.py
+++ /dev/null
@@ -1,251 +0,0 @@
-
-# This is a sample template that starts up ONOS cluster, this template
-# is used as a starting script for creating functionality and performance test
-
-class SAMPstartTemplate:
-
-    def __init__( self ):
-        self.default = ''
-
-    def CASE1( self, main ):
-        import time
-        import os
-        import imp
-        import re
-
-        """
-        - Construct tests variables
-        - GIT ( optional )
-            - Checkout ONOS master branch
-            - Pull latest ONOS code
-        - Building ONOS ( optional )
-            - Install ONOS package
-            - Build ONOS package
-        """
-
-        main.case( "Constructing test variables and building ONOS package" )
-        main.step( "Constructing test variables" )
-        stepResult = main.FALSE
-
-        # Test variables
-        main.testOnDirectory = re.sub( "(/tests)$", "", main.testDir )
-        main.cellName = main.params[ 'ENV' ][ 'cellName' ]
-        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.params[ 'SCALE' ][ 'max' ] )
-        main.ONOSport = main.params[ 'CTRL' ][ 'port' ]
-        wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
-        main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
-        gitPull = main.params[ 'GIT' ][ 'pull' ]
-        main.cellData = {} # for creating cell file
-        main.CLIs = []
-        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 ) ) )
-
-        # -- INIT SECTION, ONLY RUNS ONCE -- #
-        main.startUp = imp.load_source( wrapperFile1,
-                                        main.dependencyPath +
-                                        wrapperFile1 +
-                                        ".py" )
-
-        copyResult1 = main.ONOSbench.scp( main.Mininet1,
-                                          main.dependencyPath +
-                                          main.topology,
-                                          main.Mininet1.home,
-                                          direction="to" )
-        if main.CLIs:
-            stepResult = main.TRUE
-        else:
-            main.log.error( "Did not properly created list of ONOS CLI handle" )
-            stepResult = main.FALSE
-
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully construct " +
-                                        "test variables ",
-                                 onfail="Failed to construct test variables" )
-
-        if gitPull == 'True':
-            main.step( "Building ONOS in " + gitBranch + " branch" )
-            onosBuildResult = main.startUp.onosBuild( main, gitBranch )
-            stepResult = onosBuildResult
-            utilities.assert_equals( expect=main.TRUE,
-                                     actual=stepResult,
-                                     onpass="Successfully compiled " +
-                                            "latest ONOS",
-                                     onfail="Failed to compile " +
-                                            "latest ONOS" )
-        else:
-            main.log.warn( "Did not pull new code so skipping mvn " +
-                           "clean install" )
-
-    def CASE2( self, main ):
-        """
-        - Set up cell
-            - Create cell file
-            - Set cell file
-            - Verify cell file
-        - Kill ONOS process
-        - Uninstall ONOS cluster
-        - Verify ONOS start up
-        - Install ONOS cluster
-        - Connect to cli
-        """
-
-        # main.scale[ 0 ] determines the current number of ONOS controller
-        main.numCtrls = int( main.scale[ 0 ] )
-
-        main.case( "Starting up " + str( main.numCtrls ) +
-                   " node(s) ONOS cluster" )
-
-        #kill off all onos processes
-        main.log.info( "Safety check, killing all ONOS processes" +
-                       " before initiating environment setup" )
-
-        for i in range( main.maxNodes ):
-            main.ONOSbench.onosDie( main.ONOSip[ i ] )
-
-        print "NODE COUNT = ", main.numCtrls
-
-        tempOnosIp = []
-        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.step( "Apply cell to environment" )
-        cellResult = main.ONOSbench.setCell( "temp" )
-        verifyResult = main.ONOSbench.verifyCell()
-        stepResult = cellResult and verifyResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully applied cell to " + \
-                                        "environment",
-                                 onfail="Failed to apply cell to environment " )
-
-        main.step( "Creating ONOS package" )
-        packageResult = main.ONOSbench.onosPackage()
-        stepResult = packageResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully created ONOS package",
-                                 onfail="Failed to create ONOS package" )
-
-        time.sleep( main.startUpSleep )
-        main.step( "Uninstalling ONOS package" )
-        onosUninstallResult = main.TRUE
-        for i in range( main.numCtrls ):
-            onosUninstallResult = onosUninstallResult and \
-                    main.ONOSbench.onosUninstall( nodeIp=main.ONOSip[ i ] )
-        stepResult = onosUninstallResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully uninstalled ONOS package",
-                                 onfail="Failed to uninstall ONOS package" )
-
-        time.sleep( main.startUpSleep )
-        main.step( "Installing ONOS package" )
-        onosInstallResult = main.TRUE
-        for i in range( main.numCtrls ):
-            onosInstallResult = onosInstallResult and \
-                    main.ONOSbench.onosInstall( node=main.ONOSip[ i ] )
-        stepResult = onosInstallResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully installed ONOS package",
-                                 onfail="Failed to install ONOS package" )
-
-        time.sleep( main.startUpSleep )
-        main.step( "Starting ONOS service" )
-        stopResult = main.TRUE
-        startResult = main.TRUE
-        onosIsUp = main.TRUE
-
-        for i in range( main.numCtrls ):
-            onosIsUp = onosIsUp and main.ONOSbench.isup( main.ONOSip[ i ] )
-        if onosIsUp == main.TRUE:
-            main.log.report( "ONOS instance is up and ready" )
-        else:
-            main.log.report( "ONOS instance may not be up, stop and " +
-                             "start ONOS again " )
-            for i in range( main.numCtrls ):
-                stopResult = stopResult and \
-                        main.ONOSbench.onosStop( main.ONOSip[ i ] )
-            for i in range( main.numCtrls ):
-                startResult = startResult and \
-                        main.ONOSbench.onosStart( main.ONOSip[ i ] )
-        stepResult = onosIsUp and stopResult and startResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="ONOS service is ready",
-                                 onfail="ONOS service did not start properly" )
-
-        main.step( "Start ONOS cli" )
-        cliResult = main.TRUE
-        for i in range( main.numCtrls ):
-            cliResult = cliResult and \
-                        main.CLIs[ i ].startOnosCli( main.ONOSip[ i ] )
-        stepResult = cliResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully start ONOS cli",
-                                 onfail="Failed to start ONOS cli" )
-
-        # Remove the first element in main.scale list
-        main.scale.remove( main.scale[ 0 ] )
-
-    def CASE9( self, main ):
-        '''
-            Report errors/warnings/exceptions
-        '''
-        main.log.info("Error report: \n" )
-        main.ONOSbench.logReport( main.ONOSip[ 0 ],
-                                  [ "INFO",
-                                    "FOLLOWER",
-                                    "WARN",
-                                    "flow",
-                                    "ERROR",
-                                    "Except" ],
-                                  "s" )
-
-    def CASE11( self, main ):
-        """
-            Start mininet
-        """
-        main.log.report( "Start Mininet topology" )
-        main.log.case( "Start Mininet topology" )
-
-        main.step( "Starting Mininet Topology" )
-        topoResult = main.Mininet1.startNet( topoFile=main.dependencyPath + main.topology )
-        stepResult = topoResult
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=stepResult,
-                                 onpass="Successfully loaded topology",
-                                 onfail="Failed to load topology" )
-        # Exit if topology did not load properly
-        if not topoResult:
-            main.cleanup()
-            main.exit()
-
-    def CASE12( self, main ):
-        """
-            Test random ONOS command
-        """
-
-        main.CLIs[ 0 ].startOnosCli( main.ONOSip[ 0 ] )
-        print main.CLIs[ 0 ].leaders()
-
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/Dependency/newFuncTopo.py b/TestON/tests/SAMP/SAMPstartTemplate2_1node/Dependency/newFuncTopo.py
similarity index 100%
rename from TestON/tests/SAMP/SAMPstartTemplate/Dependency/newFuncTopo.py
rename to TestON/tests/SAMP/SAMPstartTemplate2_1node/Dependency/newFuncTopo.py
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_1node/README b/TestON/tests/SAMP/SAMPstartTemplate2_1node/README
new file mode 100644
index 0000000..7df40db
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_1node/README
@@ -0,0 +1,2 @@
+Summary:
+        Sample test case how onos test should start up.
\ No newline at end of file
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.params b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.params
new file mode 100755
index 0000000..1bed38d
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.params
@@ -0,0 +1,64 @@
+<PARAMS>
+    <!--
+        CASE0: pull onos code - this case should be skipped on Jenkins-driven prod test
+    -->
+    <!--
+        CASE1: setup and clean test env
+    -->
+    <!--
+        CASE2: get onos warnings, errors from log
+    -->
+    <!--
+        CASE10: start a 1-node ONOS
+    -->
+    <!--
+        CASE11: Start Mininet and assign controllers
+    -->
+    <!--
+        CASE12: Sample case of using onos cli
+    -->
+    <!--
+        CASE22: Sample case of using onos rest
+    -->
+
+    <testcases>0,1,10,11,12,22,2</testcases>
+
+    <CASE0>
+        <gitPull>False</gitPull> # False or True
+        <gitBranch>master</gitBranch>
+    </CASE0>
+
+    <CASE1>
+        <NodeList>OC1,OC2,OC3</NodeList>
+        <SleepTimers>
+            <onosStartup>60</onosStartup>
+            <onosCfg>5</onosCfg>
+            <mnStartup>15</mnStartup>
+            <mnCfg>10</mnCfg>
+        </SleepTimers>
+    </CASE1>
+
+    <CASE10>
+        <numNodes>1</numNodes>
+        <Apps>
+            org.onosproject.openflow,org.onosproject.fwd
+        </Apps>
+        <ONOS_Configuration>
+        <org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+            <useFlowObjectives>true</useFlowObjectives>
+        </org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+        </ONOS_Configuration>
+    </CASE10>
+
+    <CASE11>
+        <path>~/OnosSystemTest/TestON/tests/SAMP/SAMPstartTemplate2_1node/Dependency/</path>
+        <topo>newFuncTopo.py</topo>
+    </CASE11>
+
+    <CASE12>
+    </CASE12>
+
+    <CASE22>
+    </CASE22>
+
+</PARAMS>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.py b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.py
new file mode 100644
index 0000000..befa623
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.py
@@ -0,0 +1,228 @@
+
+# This is a sample template that starts up ONOS cluster, this template
+# can be use as a base script for ONOS System Testing.
+
+class SAMPstartTemplate2_1node:
+
+    def __init__( self ):
+        self.default = ''
+
+
+    def CASE0(self, main):
+        '''
+            Pull specific ONOS branch, then Build ONOS ono ONOS Bench.
+            This step is usually skipped. Because in a Jenkins driven automated
+            test env. We want Jenkins jobs to pull&build for flexibility to handle
+            different versions of ONOS.
+        '''
+        gitPull = main.params['CASE0']['gitPull']
+        gitBranch = main.params['CASE0']['gitBranch']
+
+        main.case("Pull onos branch and build onos on Teststation.")
+
+        if gitPull == 'True':
+            main.step( "Git Checkout ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.gitCheckout( branch = gitBranch )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully checkout onos branch.",
+                                     onfail="Failed to checkout onos branch. Exiting test..." )
+            if not stepResult: main.exit()
+
+            main.step( "Git Pull on ONOS branch:" + gitBranch)
+            stepResult = main.ONOSbench.gitPull( )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully pull onos. ",
+                                     onfail="Failed to pull onos. Exiting test ..." )
+            if not stepResult: main.exit()
+
+            main.step( "Building ONOS branch: " + gitBranch )
+            stepResult = main.ONOSbench.cleanInstall( skipTest = True )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully build onos.",
+                                     onfail="Failed to build onos. Exiting test..." )
+            if not stepResult: main.exit()
+
+        else:
+            main.log.warn( "Skipped pulling onos and Skipped building ONOS" )
+
+
+    def CASE1( self, main ):
+        '''
+            Set up global test variables;
+            Uninstall all running cells in test env defined in .topo file
+
+        '''
+
+        main.case( "Constructing global test variables and clean cluster env." )
+
+        main.step( "Constructing test variables" )
+        main.branch = main.ONOSbench.getBranchName()
+        main.log.info( "Running onos branch: " + main.branch )
+        main.commitNum = main.ONOSbench.getVersion().split(' ')[1]
+        main.log.info( "Running onos commit Number: " + main.commitNum)
+        main.nodeList = main.params['CASE1']['NodeList'].split(",")
+        main.onosStartupSleep = float( main.params['CASE1']['SleepTimers']['onosStartup'] )
+        main.onosCfgSleep = float( main.params['CASE1']['SleepTimers']['onosCfg'] )
+        main.mnStartupSleep = float( main.params['CASE1']['SleepTimers']['mnStartup'] )
+        main.mnCfgSleep = float( main.params['CASE1']['SleepTimers']['mnCfg'] )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.TRUE,
+                                 onpass="Successfully construct " +
+                                        "test variables ",
+                                 onfail="Failed to construct test variables" )
+
+
+
+        main.step( "Uninstall all onos nodes in the env.")
+        stepResult = main.TRUE
+        for node in main.nodeList:
+            nodeResult = main.ONOSbench.onosUninstall( nodeIp = "$" + node )
+            stepResult = stepResult & nodeResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully uninstall onos on all nodes in env.",
+                                 onfail="Failed to uninstall onos on all nodes in env!" )
+        if not stepResult:
+            main.log.error( "Failure to clean test env. Exiting test..." )
+            main.exit()
+
+    def CASE2( self, main ):
+        '''
+            Report errors/warnings/exceptions
+        '''
+        main.log.info("Error report: \n" )
+        main.ONOSbench.logReport( main.ONOScli1.ip_address,
+                                  [ "INFO",
+                                    "FOLLOWER",
+                                    "WARN",
+                                    "flow",
+                                    "ERROR",
+                                    "Except" ],
+                                  "s" )
+
+    def CASE10( self, main ):
+        """
+        Start ONOS cluster (3 nodes in this example) in three steps:
+        1) start a basic cluster with drivers app via ONOSDriver;
+        2) activate apps via ONOSCliDriver;
+        3) configure onos via ONOSCliDriver;
+        """
+
+        import time
+
+        numNodes = int( main.params['CASE10']['numNodes'] )
+        main.case( "Start up " + str( numNodes ) + "-node onos cluster.")
+
+        main.step( "Start ONOS cluster with basic (drivers) app.")
+        onosClusterIPs = []
+        for n in range( 1, numNodes + 1 ):
+            handle = "main.ONOScli" + str( n )
+            onosClusterIPs.append( eval( handle ).ip_address )
+
+        stepResult = main.ONOSbench.startBasicONOS(nodeList = onosClusterIPs, opSleep = 200 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully started basic ONOS cluster ",
+                                 onfail="Failed to start basic ONOS Cluster " )
+
+        main.step( "Establishing Handles on ONOS CLIs.")
+        cliResult = main.TRUE
+        for n in range( 1, numNodes + 1 ):
+            handle = "main.ONOScli" + str( n )
+            cliResult = cliResult & ( eval( handle ).startCellCli() )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cliResult,
+                                 onpass="Successfully started onos cli's ",
+                                 onfail="Failed to start onos cli's " )
+
+        main.step( "Activate onos apps.")
+        apps = main.params['CASE10'].get( 'Apps' )
+        if apps:
+            main.log.info( "Apps to activate: " + apps )
+            activateResult = main.TRUE
+            for a in apps.split(","):
+                activateResult = activateResult & main.ONOScli1.activateApp(a)
+            # TODO: check this worked
+            time.sleep( main.onosCfgSleep )  # wait for apps to activate
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+        utilities.assert_equals( expect=main.TRUE,
+                                     actual=activateResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+
+        main.step( "Set ONOS configurations" )
+        config = main.params['CASE10'].get( 'ONOS_Configuration' )
+        if config:
+            main.log.debug( config )
+            checkResult = main.TRUE
+            for component in config:
+                for setting in config[component]:
+                    value = config[component][setting]
+                    check = main.ONOScli1.setCfg( component, setting, value )
+                    main.log.info( "Value was changed? {}".format( main.TRUE == check ) )
+                    checkResult = check and checkResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=checkResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+
+    def CASE11( self, main ):
+        """
+            Start mininet and assign controllers
+        """
+        import time
+
+        dependencyPath = main.params['CASE11']['path']
+        topology = main.params['CASE11']['topo']
+        main.log.report( "Start Mininet topology" )
+        main.log.case( "Start Mininet topology" )
+
+        main.step( "Starting Mininet Topology" )
+        topoResult = main.Mininet1.startNet( topoFile=dependencyPath + topology )
+        stepResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanup()
+            main.exit()
+
+        main.step( "Assign switches to controllers.")
+        assignResult = main.TRUE
+        onosNodes = [ main.ONOScli1.ip_address ]
+        for i in range(1, 8):
+            assignResult = assignResult & main.Mininet1.assignSwController( sw="s" + str( i ),
+                                                         ip=onosNodes,
+                                                         port='6653' )
+        time.sleep(main.mnCfgSleep)
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully assign switches to controllers",
+                                 onfail="Failed to assign switches to controllers" )
+
+
+    def CASE12( self, main ):
+        """
+            Tests using through ONOS CLI handles
+        """
+
+        main.log.case( "Test some onos commands through CLI. ")
+        main.log.debug( main.ONOScli1.sendline("summary") )
+        main.log.debug( main.ONOScli1.sendline("devices") )
+
+    def CASE22( self, main ):
+        """
+            Tests using ONOS REST API handles
+        """
+
+        main.case( " Sample tests using ONOS REST API handles. ")
+        main.log.debug( main.ONOSrest1.send("/devices") )
+        main.log.debug( main.ONOSrest1.apps() )
\ No newline at end of file
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.topo b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.topo
similarity index 64%
rename from TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.topo
rename to TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.topo
index 068bfdd..a804d4a 100755
--- a/TestON/tests/SAMP/SAMPstartTemplate/SAMPstartTemplate.topo
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_1node/SAMPstartTemplate2_1node.topo
@@ -1,6 +1,10 @@
 <TOPOLOGY>
     <COMPONENT>
-
+    <!--
+        This is a list of all components and their handles in the test setup.
+        Even with some handles not used in test cases, we want to define
+        all onos cells here, for cases to set up onos cluster.
+    -->
         <ONOSbench>
             <host>localhost</host>
             <user>sdn</user>
@@ -8,11 +12,12 @@
             <type>OnosDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
+                <home></home> #defines where onos home is
             </COMPONENTS>
         </ONOSbench>
 
         <ONOScli1>
-            <host>localhost</host>
+            <host>OC1</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>OnosCliDriver</type>
@@ -21,28 +26,8 @@
             </COMPONENTS>
         </ONOScli1>
 
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-            </COMPONENTS>
-        </ONOScli2>
-
-         <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-            </COMPONENTS>
-        </ONOScli3>
-
         <Mininet1>
-            <host>OCN</host>
+            <host>localhost</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
@@ -52,5 +37,16 @@
             </COMPONENTS>
         </Mininet1>
 
+        <ONOSrest1>
+            <host>OC1</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest1>
+
     </COMPONENT>
 </TOPOLOGY>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/SAMP/SAMPstartTemplate2_1node/__init__.py
similarity index 100%
rename from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
rename to TestON/tests/SAMP/SAMPstartTemplate2_1node/__init__.py
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/Dependency/newFuncTopo.py b/TestON/tests/SAMP/SAMPstartTemplate2_3node/Dependency/newFuncTopo.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/Dependency/newFuncTopo.py
copy to TestON/tests/SAMP/SAMPstartTemplate2_3node/Dependency/newFuncTopo.py
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_3node/README b/TestON/tests/SAMP/SAMPstartTemplate2_3node/README
new file mode 100644
index 0000000..7df40db
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_3node/README
@@ -0,0 +1,2 @@
+Summary:
+        Sample test case how onos test should start up.
\ No newline at end of file
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.params b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.params
new file mode 100755
index 0000000..41fedd0
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.params
@@ -0,0 +1,64 @@
+<PARAMS>
+    <!--
+        CASE0: pull onos code - this case should be skipped on Jenkins-driven prod test
+    -->
+    <!--
+        CASE1: setup and clean test env
+    -->
+    <!--
+        CASE2: get onos warnings, errors from log
+    -->
+    <!--
+        CASE10: start a 3-node ONOS Cluster
+    -->
+    <!--
+        CASE11: Start Mininet and assign controllers
+    -->
+    <!--
+        CASE12: Sample case of using onos cli
+    -->
+    <!--
+        CASE22: Sample case of using onos rest
+    -->
+
+    <testcases>0,1,10,11,12,22,2</testcases>
+
+    <CASE0>
+        <gitPull>False</gitPull> # False or True
+        <gitBranch>master</gitBranch>
+    </CASE0>
+
+    <CASE1>
+        <NodeList>OC1,OC2,OC3</NodeList>
+        <SleepTimers>
+            <onosStartup>60</onosStartup>
+            <onosCfg>5</onosCfg>
+            <mnStartup>15</mnStartup>
+            <mnCfg>10</mnCfg>
+        </SleepTimers>
+    </CASE1>
+
+    <CASE10>
+        <numNodes>3</numNodes>
+        <Apps>
+            org.onosproject.openflow,org.onosproject.fwd
+        </Apps>
+        <ONOS_Configuration>
+        <org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+            <useFlowObjectives>true</useFlowObjectives>
+        </org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator>
+        </ONOS_Configuration>
+    </CASE10>
+
+    <CASE11>
+        <path>~/OnosSystemTest/TestON/tests/SAMP/SAMPstartTemplate2_3node/Dependency/</path>
+        <topo>newFuncTopo.py</topo>
+    </CASE11>
+
+    <CASE12>
+    </CASE12>
+
+    <CASE22>
+    </CASE22>
+
+</PARAMS>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.py b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.py
new file mode 100644
index 0000000..49b72b5
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.py
@@ -0,0 +1,228 @@
+
+# This is a sample template that starts up ONOS cluster, this template
+# can be use as a base script for ONOS System Testing.
+
+class SAMPstartTemplate2_3node:
+
+    def __init__( self ):
+        self.default = ''
+
+
+    def CASE0(self, main):
+        '''
+            Pull specific ONOS branch, then Build ONOS ono ONOS Bench.
+            This step is usually skipped. Because in a Jenkins driven automated
+            test env. We want Jenkins jobs to pull&build for flexibility to handle
+            different versions of ONOS.
+        '''
+        gitPull = main.params['CASE0']['gitPull']
+        gitBranch = main.params['CASE0']['gitBranch']
+
+        main.case("Pull onos branch and build onos on Teststation.")
+
+        if gitPull == 'True':
+            main.step( "Git Checkout ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.gitCheckout( branch = gitBranch )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully checkout onos branch.",
+                                     onfail="Failed to checkout onos branch. Exiting test..." )
+            if not stepResult: main.exit()
+
+            main.step( "Git Pull on ONOS branch:" + gitBranch)
+            stepResult = main.ONOSbench.gitPull( )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully pull onos. ",
+                                     onfail="Failed to pull onos. Exiting test ..." )
+            if not stepResult: main.exit()
+
+            main.step( "Building ONOS branch: " + gitBranch )
+            stepResult = main.ONOSbench.cleanInstall( skipTest = True )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully build onos.",
+                                     onfail="Failed to build onos. Exiting test..." )
+            if not stepResult: main.exit()
+
+        else:
+            main.log.warn( "Skipped pulling onos and Skipped building ONOS" )
+
+
+    def CASE1( self, main ):
+        '''
+            Set up global test variables;
+            Uninstall all running cells in test env defined in .topo file
+
+        '''
+
+        main.case( "Constructing global test variables and clean cluster env." )
+
+        main.step( "Constructing test variables" )
+        main.branch = main.ONOSbench.getBranchName()
+        main.log.info( "Running onos branch: " + main.branch )
+        main.commitNum = main.ONOSbench.getVersion().split(' ')[1]
+        main.log.info( "Running onos commit Number: " + main.commitNum)
+        main.nodeList = main.params['CASE1']['NodeList'].split(",")
+        main.onosStartupSleep = float( main.params['CASE1']['SleepTimers']['onosStartup'] )
+        main.onosCfgSleep = float( main.params['CASE1']['SleepTimers']['onosCfg'] )
+        main.mnStartupSleep = float( main.params['CASE1']['SleepTimers']['mnStartup'] )
+        main.mnCfgSleep = float( main.params['CASE1']['SleepTimers']['mnCfg'] )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.TRUE,
+                                 onpass="Successfully construct " +
+                                        "test variables ",
+                                 onfail="Failed to construct test variables" )
+
+
+
+        main.step( "Uninstall all onos nodes in the env.")
+        stepResult = main.TRUE
+        for node in main.nodeList:
+            nodeResult = main.ONOSbench.onosUninstall( nodeIp = "$" + node )
+            stepResult = stepResult & nodeResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully uninstall onos on all nodes in env.",
+                                 onfail="Failed to uninstall onos on all nodes in env!" )
+        if not stepResult:
+            main.log.error( "Failure to clean test env. Exiting test..." )
+            main.exit()
+
+    def CASE2( self, main ):
+        '''
+            Report errors/warnings/exceptions
+        '''
+        main.log.info("Error report: \n" )
+        main.ONOSbench.logReport( main.ONOScli1.ip_address,
+                                  [ "INFO",
+                                    "FOLLOWER",
+                                    "WARN",
+                                    "flow",
+                                    "ERROR",
+                                    "Except" ],
+                                  "s" )
+
+    def CASE10( self, main ):
+        """
+        Start ONOS cluster (3 nodes in this example) in three steps:
+        1) start a basic cluster with drivers app via ONOSDriver;
+        2) activate apps via ONOSCliDriver;
+        3) configure onos via ONOSCliDriver;
+        """
+
+        import time
+
+        numNodes = int( main.params['CASE10']['numNodes'] )
+        main.case( "Start up " + str( numNodes ) + "-node onos cluster.")
+
+        main.step( "Start ONOS cluster with basic (drivers) app.")
+        onosClusterIPs = []
+        for n in range( 1, numNodes + 1 ):
+            handle = "main.ONOScli" + str( n )
+            onosClusterIPs.append( eval( handle ).ip_address )
+
+        stepResult = main.ONOSbench.startBasicONOS(nodeList = onosClusterIPs, opSleep = 200 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully started basic ONOS cluster ",
+                                 onfail="Failed to start basic ONOS Cluster " )
+
+        main.step( "Establishing Handles on ONOS CLIs.")
+        cliResult = main.TRUE
+        for n in range( 1, numNodes + 1 ):
+            handle = "main.ONOScli" + str( n )
+            cliResult = cliResult & ( eval( handle ).startCellCli() )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=cliResult,
+                                 onpass="Successfully started onos cli's ",
+                                 onfail="Failed to start onos cli's " )
+
+        main.step( "Activate onos apps.")
+        apps = main.params['CASE10'].get( 'Apps' )
+        if apps:
+            main.log.info( "Apps to activate: " + apps )
+            activateResult = main.TRUE
+            for a in apps.split(","):
+                activateResult = activateResult & main.ONOScli1.activateApp(a)
+            # TODO: check this worked
+            time.sleep( main.onosCfgSleep )  # wait for apps to activate
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+        utilities.assert_equals( expect=main.TRUE,
+                                     actual=activateResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+
+        main.step( "Set ONOS configurations" )
+        config = main.params['CASE10'].get( 'ONOS_Configuration' )
+        if config:
+            main.log.debug( config )
+            checkResult = main.TRUE
+            for component in config:
+                for setting in config[component]:
+                    value = config[component][setting]
+                    check = main.ONOScli1.setCfg( component, setting, value )
+                    main.log.info( "Value was changed? {}".format( main.TRUE == check ) )
+                    checkResult = check and checkResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=checkResult,
+                                     onpass="Successfully set config",
+                                     onfail="Failed to set config" )
+        else:
+            main.log.warn( "No configurations were specified to be changed after startup" )
+
+    def CASE11( self, main ):
+        """
+            Start mininet and assign controllers
+        """
+        import time
+
+        dependencyPath = main.params['CASE11']['path']
+        topology = main.params['CASE11']['topo']
+        main.log.report( "Start Mininet topology" )
+        main.log.case( "Start Mininet topology" )
+
+        main.step( "Starting Mininet Topology" )
+        topoResult = main.Mininet1.startNet( topoFile=dependencyPath + topology )
+        stepResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanup()
+            main.exit()
+
+        main.step( "Assign switches to controllers.")
+        assignResult = main.TRUE
+        onosNodes = [ main.ONOScli1.ip_address, main.ONOScli2.ip_address, main.ONOScli3.ip_address ]
+        for i in range(1, 8):
+            assignResult = assignResult & main.Mininet1.assignSwController( sw="s" + str( i ),
+                                                         ip=onosNodes,
+                                                         port='6653' )
+        time.sleep(main.mnCfgSleep)
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully assign switches to controllers",
+                                 onfail="Failed to assign switches to controllers" )
+
+
+    def CASE12( self, main ):
+        """
+            Tests using through ONOS CLI handles
+        """
+
+        main.log.case( "Test some onos commands through CLI. ")
+        main.log.debug( main.ONOScli1.sendline("summary") )
+        main.log.debug( main.ONOScli3.sendline("devices") )
+
+    def CASE22( self, main ):
+        """
+            Tests using ONOS REST API handles
+        """
+
+        main.case( " Sample tests using ONOS REST API handles. ")
+        main.log.debug( main.ONOSrest1.send("/devices") )
+        main.log.debug( main.ONOSrest2.apps() )
diff --git a/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.topo b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.topo
new file mode 100755
index 0000000..9f23369
--- /dev/null
+++ b/TestON/tests/SAMP/SAMPstartTemplate2_3node/SAMPstartTemplate2_3node.topo
@@ -0,0 +1,94 @@
+<TOPOLOGY>
+    <COMPONENT>
+    <!--
+        This is a list of all components and their handles in the test setup.
+        Even with some handles not used in test cases, we want to define
+        all onos cells here, for cases to set up onos cluster.
+    -->
+        <ONOSbench>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+                <home></home> #defines where onos home is
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOScli1>
+            <host>OC1</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOScli1>
+
+        <ONOScli2>
+            <host>OC2</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOScli2>
+
+         <ONOScli3>
+            <host>OC3</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOScli3>
+
+        <Mininet1>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/custom/</home>
+            </COMPONENTS>
+        </Mininet1>
+
+        <ONOSrest1>
+            <host>OC1</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest1>
+
+        <ONOSrest2>
+            <host>OC2</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>7</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest2>
+
+         <ONOSrest3>
+            <host>OC3</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>8</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest3>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/SAMP/SAMPstartTemplate2_3node/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/SAMP/SAMPstartTemplate2_3node/__init__.py
diff --git a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
index 385d9e6..ac2b3c6 100644
--- a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
+++ b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
@@ -157,7 +157,7 @@
 
         time.sleep(20)
 
-        main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.store.flow.impl.NewDistributedFlowRuleStore", "backupEnabled " + str(flowRuleBU))
+        main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.store.flow.impl.DistributedFlowRuleStore", "backupEnabled " + str(flowRuleBU))
         main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.net.intent.impl.IntentManager", "skipReleaseResourcesOnWithdrawal " + skipRelRsrc)
         devices = int(clusterCount)*10
 
diff --git a/TestON/tests/SCPF/SCPFintentEventTpWithFlowObj/SCPFintentEventTpWithFlowObj.py b/TestON/tests/SCPF/SCPFintentEventTpWithFlowObj/SCPFintentEventTpWithFlowObj.py
index f3cd037..059552b 100644
--- a/TestON/tests/SCPF/SCPFintentEventTpWithFlowObj/SCPFintentEventTpWithFlowObj.py
+++ b/TestON/tests/SCPF/SCPFintentEventTpWithFlowObj/SCPFintentEventTpWithFlowObj.py
@@ -158,7 +158,7 @@
 
         time.sleep(20)
 
-        main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.store.flow.impl.NewDistributedFlowRuleStore", "backupEnabled " + str(flowRuleBU))
+        main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.store.flow.impl.DistributedFlowRuleStore", "backupEnabled " + str(flowRuleBU))
         main.ONOSbench.onosCfgSet( ONOSIp[0], "org.onosproject.net.intent.impl.IntentManager", "skipReleaseResourcesOnWithdrawal " + skipRelRsrc)
         devices = int(clusterCount)*10
 
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
index 3799982..9dfa7a4 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
@@ -1,6 +1,6 @@
 <PARAMS>
 
-    <testcases>1,2,1,2,1,2,1,2</testcases>
+    <testcases>0,1,2,1,2,1,2,1,2</testcases>
 
     <SCALE>1,3,5,7</SCALE>
     <max>7</max>
@@ -14,30 +14,33 @@
         <skipCleanInstall>yes</skipCleanInstall>
         <warmUp>5</warmUp>
         <sampleSize>20</sampleSize>
-        <wait></wait>
         <intents>1,100,1000</intents>                       #list format, will be split on ','
-        <debug>True</debug>
-
-        <s1>1,1,1,1,1,1,1,1</s1>
-        <s3>2,2,1,1,3,3,3,1</s3>
-        <s5>2,2,1,1,3,4,5,3</s5>
-        <s7>2,3,1,1,5,6,7,4</s7>
-
+        <ingress>null:0000000000000001/0</ingress>
+        <egress>null:0000000000000007/0</egress>
+        <debug>False</debug>
     </TEST>
-
-    <METRICS>
-        <Submitted>0</Submitted>
-        <Installed>1</Installed>
-        <Failed>0</Failed>
-        <Withdraw>0</Withdraw>
-        <Withdrawn>0</Withdrawn>
-    </METRICS>
+    <DATABASE>
+        <file>/tmp/IntentRerouteLatDB</file>
+    </DATABASE>
 
     <GIT>
-        <autopull>off</autopull>
-        <checkout>master</checkout>
+        <gitPull>False</gitPull>
+        <gitBranch>master</gitBranch>
     </GIT>
 
+    <ATTEMPTS>
+        <verify>3</verify>
+    </ATTEMPTS>
+
+    <SLEEP>
+        <startup>10</startup>
+        <install>10</install>
+        <verify>3</verify>
+        <reroute>3</reroute>
+        # timeout for pexpect
+        <timeout>300</timeout>
+    </SLEEP>
+
     <CTRL>
         <USER>sdn</USER>
 
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
index 7d70d7f..182f4b6 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
@@ -1,419 +1,366 @@
-# ScaleOutTemplate
-#
-# CASE1 starts number of nodes specified in param file
-#
-# cameron@onlab.us
-
-import sys
-import os.path
-
+# SCPFintentRerouteLat
+"""
+SCPFintentRerouteLat
+    - Test Intent Reroute Latency
+    - Test Algorithm:
+        1. Start Null Provider reroute Topology
+        2. Using Push-test-intents to push batch size intents from switch 1 to switch 7
+        3. Cut the link between switch 3 and switch 4 (the path will reroute to switch 8)
+        4. Get the topology time stamp
+        5. Get Intent reroute(Installed) time stamp from each nodes
+        6. Use the latest intent time stamp subtract topology time stamp
+    - This test will run 5 warm up by default, warm up iteration can be setup in Param file
+    - The intent batch size will default set to 1, 100, and 1000, also can be set in Param file
+    - The unit of the latency result is milliseconds
+"""
 
 class SCPFintentRerouteLat:
-
-    def __init__( self ):
+    def __init__(self):
         self.default = ''
 
-    def CASE1( self, main ):
+    def CASE0( self, main ):
+        '''
+        - GIT
+        - BUILDING ONOS
+            Pull specific ONOS branch, then Build ONOS ono ONOS Bench.
+            This step is usually skipped. Because in a Jenkins driven automated
+            test env. We want Jenkins jobs to pull&build for flexibility to handle
+            different versions of ONOS.
+        - Construct tests variables
+        '''
+        gitPull = main.params['GIT']['gitPull']
+        gitBranch = main.params['GIT']['gitBranch']
 
+        main.case("Pull onos branch and build onos on Teststation.")
+
+        if gitPull == 'True':
+            main.step("Git Checkout ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.gitCheckout(branch=gitBranch)
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully checkout onos branch.",
+                                    onfail="Failed to checkout onos branch. Exiting test...")
+            if not stepResult: main.exit()
+
+            main.step("Git Pull on ONOS branch:" + gitBranch)
+            stepResult = main.ONOSbench.gitPull()
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully pull onos. ",
+                                    onfail="Failed to pull onos. Exiting test ...")
+            if not stepResult: main.exit()
+
+            main.step("Building ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.cleanInstall(skipTest=True)
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully build onos.",
+                                    onfail="Failed to build onos. Exiting test...")
+            if not stepResult: main.exit()
+
+        else:
+            main.log.warn("Skipped pulling onos and Skipped building ONOS")
+
+        main.apps = main.params['ENV']['cellApps']
+        main.BENCHUser = main.params['BENCH']['user']
+        main.BENCHIp = main.params['BENCH']['ip1']
+        main.MN1Ip = main.params['MN']['ip1']
+        main.maxNodes = int(main.params['max'])
+        main.skipMvn = main.params['TEST']['skipCleanInstall']
+        main.cellName = main.params['ENV']['cellName']
+        main.scale = (main.params['SCALE']).split(",")
+        main.dbFileName = main.params['DATABASE']['file']
+        main.timeout = int(main.params['SLEEP']['timeout'])
+        main.startUpSleep = int(main.params['SLEEP']['startup'])
+        main.installSleep = int(main.params['SLEEP']['install'])
+        main.verifySleep = int(main.params['SLEEP']['verify'])
+        main.verifyAttempts = int(main.params['ATTEMPTS']['verify'])
+        main.sampleSize = int(main.params['TEST']['sampleSize'])
+        main.warmUp = int(main.params['TEST']['warmUp'])
+        main.intentsList = (main.params['TEST']['intents']).split(",")
+        main.ingress = main.params['TEST']['ingress']
+        main.egress = main.params['TEST']['egress']
+        main.debug = main.params['TEST']['debug']
+        for i in range(0, len(main.intentsList)):
+            main.intentsList[i] = int(main.intentsList[i])
+            # Create DataBase file
+        main.log.info("Create Database file " + main.dbFileName)
+        resultsDB = open(main.dbFileName, "w+")
+        resultsDB.close()
+
+    def CASE1( self, main ):
+        '''
+            clean up test environment and set up
+        '''
         import time
 
-        global init
-        try:
-            if type(init) is not bool:
-                init = False
-        except NameError:
-            init = False
+        main.log.info("Get ONOS cluster IP")
+        print(main.scale)
+        main.numCtrls = int(main.scale[0])
+        main.ONOSip = []
+        main.maxNumBatch = 0
+        main.AllONOSip = main.ONOSbench.getOnosIps()
+        for i in range(main.numCtrls):
+            main.ONOSip.append(main.AllONOSip[i])
+        main.log.info(main.ONOSip)
+        main.CLIs = []
+        main.log.info("Creating list of ONOS cli handles")
+        for i in range(main.numCtrls):
+            main.CLIs.append(getattr(main, 'ONOS%scli' % (i + 1)))
 
-        #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' ]
-        BENCHUser = main.params[ 'BENCH' ][ 'user' ]
-        BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
-        MN1Ip = main.params[ 'MN' ][ 'ip1' ]
-        main.maxNodes = int(main.params[ 'max' ])
-        skipMvn = main.params[ 'TEST' ][ 'skipCleanInstall' ]
-        cellName = main.params[ 'ENV' ][ 'cellName' ]
+        if not main.CLIs:
+            main.log.error("Failed to create the list of ONOS cli handles")
+            main.cleanup()
+            main.exit()
 
-        # -- 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
+        main.commit = main.ONOSbench.getVersion(report=True)
+        main.commit = main.commit.split(" ")[1]
+        main.log.info("Starting up %s node(s) ONOS cluster" % main.numCtrls)
+        main.log.info("Safety check, killing all ONOS processes" +
+                      " before initiating environment setup")
 
-            clusterCount = 0
-            ONOSIp = [ 0 ]
-            scale = (main.params[ 'SCALE' ]).split(",")
-            clusterCount = int(scale[0])
+        for i in range(main.numCtrls):
+            main.ONOSbench.onosDie(main.ONOSip[i])
 
-            #Populate ONOSIp with ips from params
-            ONOSIp = [0]
-            ONOSIp.extend(main.ONOSbench.getOnosIps())
+        main.log.info("NODE COUNT = %s" % main.numCtrls)
 
-            print("-----------------" + str(ONOSIp))
-            #mvn clean install, for debugging set param 'skipCleanInstall' to yes to speed up test
-            if skipMvn != "yes":
-                mvnResult = main.ONOSbench.cleanInstall()
+        # tempOnosIp = []
+        # for i in range(main.numCtrls):
+        #    tempOnosIp.append(main.AllONOSip[i])
+        # print(tempOnosIp)
+        main.ONOSbench.createCellFile(main.ONOSbench.ip_address,
+                                      main.cellName,
+                                      main.MN1Ip,
+                                      main.apps,
+                                      main.ONOSip)
+        main.step("Apply cell to environment")
+        cellResult = main.ONOSbench.setCell(main.cellName)
+        verifyResult = main.ONOSbench.verifyCell()
+        stepResult = cellResult and verifyResult
+        utilities.assert_equals(expect=main.TRUE,
+                                actual=stepResult,
+                                onpass="Successfully applied cell to " + \
+                                       "environment",
+                                onfail="Failed to apply cell to environment ")
 
-            #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" )
-
-            commit = main.ONOSbench.getVersion()
-            commit = (commit.split(" "))[1]
-
-            resultsDB = open("/tmp/IntentRerouteLatDB", "w+")
-            resultsDB.close()
-
-        # -- END OF INIT SECTION --#
-
-        clusterCount = int(scale[0])
-        scale.remove(scale[0])
-
-        #kill off all onos processes
-        main.log.step("Safety check, killing all ONOS processes")
-        main.log.step("before initiating environment setup")
-        for node in range(1, main.maxNodes + 1):
-            main.ONOSbench.onosDie(ONOSIp[node])
-
-        #Uninstall everywhere
-        main.log.step( "Cleaning Enviornment..." )
-        for i in range(1, main.maxNodes + 1):
-            main.log.info(" Uninstalling ONOS " + str(i) )
-            main.ONOSbench.onosUninstall( ONOSIp[i] )
-
-        #construct the cell file
-        main.log.info("Creating cell file")
-        cellIp = []
-        for node in range (1, clusterCount + 1):
-            cellIp.append(ONOSIp[node])
-
-        main.ONOSbench.createCellFile(BENCHIp,cellName,MN1Ip,str(Apps), cellIp)
-
-        main.step( "Set Cell" )
-        main.ONOSbench.setCell(cellName)
-
-        main.step( "Creating ONOS package" )
+        main.step("Creating ONOS package")
         packageResult = main.ONOSbench.onosPackage()
+        stepResult = packageResult
+        utilities.assert_equals(expect=main.TRUE,
+                                actual=stepResult,
+                                onpass="Successfully created ONOS package",
+                                onfail="Failed to create ONOS package")
 
-        main.step( "verify cells" )
-        verifyCellResult = main.ONOSbench.verifyCell()
+        main.step("Uninstall ONOS package on all Nodes")
+        uninstallResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("Uninstalling package on ONOS Node IP: " + main.ONOSip[i])
+            u_result = main.ONOSbench.onosUninstall(main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=u_result,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            uninstallResult = (uninstallResult and u_result)
 
-        main.log.report( "Initializing " + str( clusterCount ) + " node cluster." )
-        for node in range(1, clusterCount + 1):
-            main.log.info("Starting ONOS " + str(node) + " at IP: " + ONOSIp[node])
-            main.ONOSbench.onosInstall( ONOSIp[node])
+        main.step("Install ONOS package on all Nodes")
+        installResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("Installing package on ONOS Node IP: " + main.ONOSip[i])
+            i_result = main.ONOSbench.onosInstall(node=main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=i_result,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            installResult = installResult and i_result
 
-        for node in range(1, clusterCount + 1):
-            for i in range( 2 ):
-                isup = main.ONOSbench.isup( ONOSIp[node] )
-                if isup:
-                    main.log.info("ONOS " + str(node) + " is up\n")
-                    break
-            if not isup:
-                main.log.report( "ONOS " + str(node) + " didn't start!" )
-        main.log.info("Startup sequence complete")
+        main.step("Verify ONOS nodes UP status")
+        statusResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("ONOS Node " + main.ONOSip[i] + " status:")
+            onos_status = main.ONOSbench.onosStatus(node=main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=onos_status,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            statusResult = (statusResult and onos_status)
+        time.sleep(2)
+        main.step("Start ONOS CLI on all nodes")
+        cliResult = main.TRUE
+        main.log.step(" Start ONOS cli using thread ")
+        startCliResult = main.TRUE
+        pool = []
+        main.threadID = 0
+        for i in range(int(main.numCtrls)):
+            t = main.Thread(target=main.CLIs[i].startOnosCli,
+                            threadID=main.threadID,
+                            name="startOnosCli",
+                            args=[main.ONOSip[i]],
+                            kwargs={"onosStartTimeout": main.timeout})
+            pool.append(t)
+            t.start()
+            main.threadID = main.threadID + 1
+        for t in pool:
+            t.join()
+            startCliResult = startCliResult and t.result
+        time.sleep(main.startUpSleep)
 
-        deviceMastership = (main.params[ 'TEST' ][ "s" + str(clusterCount) ]).split(",")
-        print("Device mastership list: " + str(deviceMastership))
+        # configure apps
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "deviceCount", value=8)
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "topoShape", value="reroute")
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "enabled", value="true")
+        main.CLIs[0].setCfg("org.onosproject.store.flow.impl.DistributedFlowRuleStore", "backupEnabled", value="false")
 
-        main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.store.flow.impl.NewDistributedFlowRuleStore", "backupEnabled false")
+        time.sleep(main.startUpSleep)
 
-        main.log.step("Setting up null provider")
-        for i in range(3):
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "deviceCount 8")
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "topoShape reroute")
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "enabled true")
-            time.sleep(5)
-            main.ONOSbench.handle.sendline("onos $OC1 summary")
-            main.ONOSbench.handle.expect(":~")
-            x = main.ONOSbench.handle.before
-            if "devices=8" in x and "links=16," in x:
-                break
-
-        index = 1
-        for node in deviceMastership:
-            for attempt in range(0,10):
-                cmd = ( "onos $OC" + node + """ "device-role null:000000000000000""" + str(index) + " " + ONOSIp[int(node)]  + """ master" """)
-                main.log.info("assigning mastership of device " + str(index) + " to node " + node + ": \n " + cmd + "\n")
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                time.sleep(4)
-
-                cmd = ( "onos $OC" + node + " roles|grep 00000" + str(index))
-                main.log.info(cmd)
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                check = main.ONOSbench.handle.before
-                main.log.info("CHECK:\n" + check)
-                if ("master=" + ONOSIp[int(node)]) in check:
-                    break
-            index += 1
-
-        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"])
+        # Balance Master
+        main.CLIs[0].balanceMasters()
+        if len(main.ONOSip) > 1:
+            main.CLIs[0].deviceRole("null:0000000000000003", main.ONOSip[0])
+            main.CLIs[0].deviceRole("null:0000000000000004", main.ONOSip[0])
 
     def CASE2( self, main ):
-
         import time
         import numpy
         import datetime
-        #from scipy import stats
+        import json
+        # from scipy import stats
 
         ts = time.time()
-
-        sampleSize = int(main.params[ 'TEST' ][ 'sampleSize' ])
-        warmUp = int(main.params[ 'TEST' ][ 'warmUp' ])
-        intentsList = (main.params[ 'TEST' ][ 'intents' ]).split(",")
-        debug = main.params[ 'TEST' ][ 'debug' ]
-        for i in range(0,len(intentsList)):
-            intentsList[i] = int(intentsList[i])
-
-        timestampMetrics = []
-        if main.params['METRICS']['Submitted'] == "1":
-            timestampMetrics.append("Submitted")
-        if main.params['METRICS']['Installed'] == "1":
-            timestampMetrics.append("Installed")
-        if main.params['METRICS']['Failed'] == "1":
-            timestampMetrics.append("Failed")
-        if main.params['METRICS']['Withdraw'] == "1":
-            timestampMetrics.append("Withdraw")
-        if main.params['METRICS']['Withdrawn'] == "1":
-            timestampMetrics.append("Withdrawn")
-        if debug: main.log.info(timestampMetrics)
-
-        if debug == "True":
-            debug = True
-        else:
-            debug = False
-
-        ingress = "null:0000000000000001"
-        egress = "null:0000000000000007"
-
-        for intents in intentsList:
-            main.log.report("Intent Batch size: " + str(intents) + "\n      ")
-            myResult = [["latency", "lastNode"] for x in range(sampleSize)]
-
-            for run in range(0, (warmUp + sampleSize)):
-                if run > warmUp:
-                    main.log.info("Starting test iteration " + str(run-warmUp))
-
-                cmd = """onos $OC1 "push-test-intents -i """
-                cmd += ingress + "/0 "
-                cmd += egress + "/0 "
-                cmd += str(intents) +""" 1" """
-                if debug: main.log.info(cmd)
-
-                withdrawCmd = cmd.replace("intents -i", "intents -w ")
-
-                #push-test-intents
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                myRawResult = main.ONOSbench.handle.before
-
-                for i in range(0, 40):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    if ("links=16,") in linkCheck and ("flows=" + str(intents*7) + ","):
-                        break
-                    if i == 39:
-                        main.log.error("Flow/link count incorrect, data invalid."+ linkCheck)
-                        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"], "d")
-                        #main.ONOSbench.logReport(ONOSIp[(clusterCount-1)], ["ERROR", "WARNING", "EXCEPT"], "d")
-                        main.ONOSbench.sendline("onos $OC1 summary")
-                        main.ONOSbench.sendline("onos $OC1 devices")
-                        main.ONOSbench.sendline("onos $OC1 links")
-                        main.ONOSbench.expect(":~")
-                        main.log.info(main.ONOSbench.before)
-
-                #collect timestamp from link cut
-                cmd = """onos $OC1 null-link "null:0000000000000004/1 null:0000000000000003/2 down" """
-                if debug: main.log.info("COMMAND: " + str(cmd))
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-
-                cmd = "onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep TopologyManager| tail -1"
-                for i in range(0,10):
-                    main.ONOSbench.handle.sendline(cmd)
-                    time.sleep(2)
-                    main.ONOSbench.handle.expect(":~")
-                    raw = main.ONOSbench.handle.before
-                    #if "NullLinkProvider" in raw and "links=14" in raw:
-                    if "links=14" in raw:
-                        break
-                    if i >= 9:
-                        main.log.error("Expected output not being recieved... continuing")
-                        main.log.info(raw)
-                        break
-                    time.sleep(2)
-
-                if debug: main.log.debug("raw: " + raw)
-
-                temp = raw.splitlines()
-
-                if debug: main.log.debug("temp (after splitlines): " + str(temp))
-
-                # Since the string is deterministic the date is always the 3rd element.
-                # However, if the data were grepping for in the onos log changes then this will
-                # not work. This is why we print out the raw and temp string so we can visually
-                # check if everything is in the correct order. temp should like this:
-                # temp = ['/onos$ onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep Top ', 
-                #         'ologyManager| tail -1', '2015-10-15 12:03:33,736 ... ]
-                temp = temp[2]
-
-                if debug: main.log.debug("temp (checking for date): " + str(temp))
-
-                cutTimestamp = (temp.split(" "))[0] + " " + (temp.split(" "))[1]
-
-                if debug: main.log.info("Cut timestamp: " + cutTimestamp)
-
-                #validate link count and flow count
-                for i in range(0, 40):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    #if "links=" + str(7*intents)+ "," in linkCheck and ("flows=" + str(7*intents) + ",") in linkCheck:
-                    if "links=14," in linkCheck and ("flows=" + str(8*intents) + ",") in linkCheck:
-                        break
-                    if i == 39:
-                        main.log.error("Link or flow count incorrect, data invalid." + linkCheck)
-                        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"], "d")
-
-                time.sleep(5) #trying to avoid negative values
-
-                #intents events metrics installed timestamp
-                IEMtimestamps = [0]*(clusterCount + 1)
-                installedTemp = [0]*(clusterCount + 1)
-                for node in range(1, clusterCount +1):
-                    cmd = "onos $OC" + str(node) + """ "intents-events-metrics"|grep Timestamp """
-                    raw = ""
-                    while "epoch)" not in raw:
-                        main.ONOSbench.handle.sendline(cmd)
-                        main.ONOSbench.handle.expect(":~")
-                        raw = main.ONOSbench.handle.before
-
-                    print(raw)
-
-                    intentsTimestamps = {}
-                    rawTimestamps = raw.splitlines()
-                    for line in rawTimestamps:
-                        if "Timestamp" in line and "grep" not in line:
-                            metricKey = (line.split(" "))[1]
-                            metricTimestamp = (line.split(" ")[len(line.split(" ")) -1]).replace("epoch)=","")
-                            metricTimestamp = float(metricTimestamp)
-                            metricTimestamp = numpy.divide(metricTimestamp, 1000)
-                            if debug: main.log.info(repr(metricTimestamp))
-                            intentsTimestamps[metricKey] = metricTimestamp
-                            if metricKey == "Installed":
-                                installedTemp[node] = metricTimestamp
-
-                    main.log.info("Node: " + str(node) + " Timestamps: " + str(intentsTimestamps))
-                    IEMtimestamps[node] = intentsTimestamps
-
-                myMax = max(installedTemp)
-                indexOfMax = installedTemp.index(myMax)
-
-                #number crunch
-                for metric in timestampMetrics:     #this is where we sould add support for computing other timestamp metrics
-                    if metric == "Installed":
-                        if run >= warmUp:
-                            main.log.report("link cut timestamp: " + cutTimestamp)
-                            #readableInstalledTimestamp = str(intentsTimestamps["Installed"])
-                            readableInstalledTimestamp = str(myMax)
-
-                            #main.log.report("Intent Installed timestamp: " + str(intentsTimestamps["Installed"]))
-                            main.log.report("Intent Installed timestamp: " + str(myMax))
-
-                            cutEpoch = time.mktime(time.strptime(cutTimestamp, "%Y-%m-%d %H:%M:%S,%f"))
-                            if debug: main.log.info("cutEpoch=" + str(cutEpoch))
-                            #rerouteLatency = float(intentsTimestamps["Installed"] - cutEpoch)
-                            rerouteLatency = float(myMax - cutEpoch)
-
-                            rerouteLatency = numpy.divide(rerouteLatency, 1000)
-                            main.log.report("Reroute latency:" + str(rerouteLatency) + " (seconds)\n    ")
-                            myResult[run-warmUp][0] = rerouteLatency
-                            myResult[run-warmUp][1] = indexOfMax
-                            if debug: main.log.info("Latency: " + str(myResult[run-warmUp][0]))
-                            if debug: main.log.info("last node: " + str(myResult[run-warmUp][1]))
-
-                cmd = """ onos $OC1 null-link "null:0000000000000004/1 null:0000000000000003/2 up" """
-                if debug: main.log.info(cmd)
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-
-                #wait for intent withdraw
-                main.ONOSbench.handle.sendline(withdrawCmd)
-                main.log.info(withdrawCmd)
-                main.ONOSbench.handle.expect(":~")
-                if debug: main.log.info(main.ONOSbench.handle.before)
-                main.ONOSbench.handle.sendline("onos $OC1 intents|grep WITHDRAWN|wc -l")
-                main.ONOSbench.handle.expect(":~")
-                intentWithdrawCheck = main.ONOSbench.handle.before
-                if (str(intents)) in intentWithdrawCheck:
-                    main.log.info("intents withdrawn")
-                if debug: main.log.info(intentWithdrawCheck)
-
-                # wait for links to be reestablished
-                for i in range(0, 10):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    if "links=16," in linkCheck:
-                        break
-                    time.sleep(1)
-                    if i == 9:
-                        main.log.info("Links Failed to reconnect, next iteration of data invalid." + linkCheck)
-
-                if run < warmUp:
-                    main.log.info("Warm up run " + str(run+1) + " completed")
-
-            if debug: main.log.info(myResult)
-            latTemp = []
-            nodeTemp = []
-            for i in myResult:
-                latTemp.append(i[0])
-                nodeTemp.append(i[1])
-
-            mode = {}
-            for i in nodeTemp:
-                if i in mode:
-                    mode[i] += 1
+        print(main.intentsList)
+        for batchSize in main.intentsList:
+            main.log.report("Intent Batch size: " + str(batchSize) + "\n      ")
+            main.LatencyList = []
+            for run in range(0, (main.warmUp + main.sampleSize)):
+                if run >= main.warmUp:
+                    main.log.info("================================================")
+                    main.log.info("Starting test iteration " + str(run - main.warmUp))
+                    main.log.info("================================================")
                 else:
-                    mode[i] = 1
+                    main.log.info("====================Warm Up=====================")
 
-            for i in mode.keys():
-                if mode[i] == max(mode.values()):
-                    nodeMode = i
+                # push intents
+                main.CLIs[0].pushTestIntents(main.ingress, main.egress, batchSize,
+                                             offset=1, options="-i", timeout=main.timeout)
 
-            average = numpy.average(latTemp)
-            stdDev = numpy.std(latTemp)
+                # check links and flows
+                k = 0
+                verify = main.FALSE
+                linkCheck = 0
+                flowsCheck = 0
+                while k <= main.verifyAttempts:
+                    time.sleep(main.verifySleep)
+                    summary = json.loads(main.CLIs[0].summary(timeout=main.timeout))
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    if linkCheck == 16 and flowsCheck == batchSize * 7:
+                        main.log.info("links: {}, flows: {} ".format(linkCheck, flowsCheck))
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.warn("Links or flows number are not match!")
+                    main.log.warn("links: {}, flows: {} ".format(linkCheck, flowsCheck))
+                    continue
 
-            average = numpy.multiply(average, 1000)
-            stdDev = numpy.multiply(stdDev, 1000)
+                # Bring link down
+                main.CLIs[0].link("0000000000000004/1", "0000000000000003/2", "down",
+                                  timeout=main.timeout, showResponse=False)
+                verify = main.FALSE
+                k = 0
+                topoManagerLog = ""
+                while k <= main.verifyAttempts:
+                    time.sleep(main.verifySleep)
+                    summary = json.loads(main.CLIs[0].summary(timeout=main.timeout))
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    if linkCheck == 14:
+                        main.log.info("links: {}, flows: {} ".format(linkCheck, flowsCheck))
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.warn("Links number are not match in TopologyManager log!")
+                    main.log.warn(topoManagerLog)
+                    continue
 
-            main.log.report("Scale: " + str(clusterCount) + "  \tIntent batch: " + str(intents))
-            main.log.report("Latency average:................" + str(average))
-            main.log.report("Latency standard deviation:....." + str(stdDev))
-            main.log.report("Mode of last node to respond:..." + str(nodeMode))
+                try:
+                    # expect twice to clean the pexpect buffer
+                    main.ONOSbench.handle.sendline("")
+                    main.ONOSbench.handle.expect("\$")
+                    main.ONOSbench.handle.expect("\$")
+                    # send line by using bench, can't use driver because pexpect buffer problem
+                    cmd = "onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep TopologyManager| tail -1"
+                    main.ONOSbench.handle.sendline(cmd)
+                    time.sleep(1)
+                    main.ONOSbench.handle.expect(":~")
+                    topoManagerLog = main.ONOSbench.handle.before
+                    topoManagerLogTemp = topoManagerLog.splitlines()
+                    # To make sure we get correct topology log
+                    for lines in topoManagerLogTemp:
+                        if "creationTime" in lines:
+                            topoManagerLog = lines
+                    main.log.info("Topology Manager log:")
+                    print(topoManagerLog)
+                    cutTimestamp = float(topoManagerLog.split("creationTime=")[1].split(",")[0])
+                except:
+                    main.log.error("Topology Log is not correct!")
+                    print(topoManagerLog)
+                    # If we got wrong Topology log, we should skip this iteration, and continue for next one
+                    continue
+
+                installedTemp = []
+                time.sleep(1)
+                for cli in main.CLIs:
+                    tempJson = json.loads(cli.intentsEventsMetrics())
+                    Installedtime = tempJson.get('intentInstalledTimestamp').get('value')
+                    installedTemp.append(float(Installedtime))
+                for i in range(0, len(installedTemp)):
+                    main.log.info("ONOS Node {} Installed Time stemp: {}".format((i + 1), installedTemp[i]))
+                maxInstallTime = float(max(installedTemp))
+                if run >= main.warmUp:
+                    main.log.info("Installed time stemp: {0:f}".format(maxInstallTime))
+                    main.log.info("CutTimestamp: {0:f}".format(cutTimestamp))
+                    # Both timeStemps are milliseconds
+                    main.log.info("Latency: {0:f}".format(float(maxInstallTime - cutTimestamp)))
+                    main.LatencyList.append(float(maxInstallTime - cutTimestamp))
+
+                # Verify Summary after we bring up link, and withdrawn intents
+                main.CLIs[0].link("0000000000000004/1", "0000000000000003/2", "up",
+                                  timeout=main.timeout)
+                k = 0
+                verify = main.FALSE
+                linkCheck = 0
+                flowsCheck = 0
+                while k <= main.verifyAttempts:
+                    time.sleep(main.verifySleep)
+                    main.CLIs[0].removeAllIntents(purge=True, sync=True, timeout=main.timeout)
+                    time.sleep(1)
+                    main.CLIs[0].purgeWithdrawnIntents()
+                    summary = json.loads(main.CLIs[0].summary())
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    intentCheck = summary.get("intents")
+                    if linkCheck == 16 and flowsCheck == 0 and intentCheck == 0:
+                        main.log.info("links: {}, flows: {}, intents: {} ".format(linkCheck, flowsCheck, intentCheck))
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.error("links, flows, or intents are not correct!")
+                    main.log.info("links: {}, flows: {}, intents: {} ".format(linkCheck, flowsCheck, intentCheck))
+                    continue
+
+            aveLatency = 0
+            stdLatency = 0
+            aveLatency = numpy.average(main.LatencyList)
+            stdLatency = numpy.std(main.LatencyList)
+            main.log.report("Scale: " + str(main.numCtrls) + "  \tIntent batch: " + str(batchSize))
+            main.log.report("Latency average:................" + str(aveLatency))
+            main.log.report("Latency standard deviation:....." + str(stdLatency))
             main.log.report("________________________________________________________")
 
-            resultsDB = open("/tmp/IntentRerouteLatDB", "a")
-            resultsDB.write("'" + commit + "',")
-            resultsDB.write(str(clusterCount) + ",")
-            resultsDB.write(str(intents) + ",")
-            resultsDB.write(str(average) + ",")
-            resultsDB.write(str(stdDev) + "\n")
+            resultsDB = open(main.dbFileName, "a")
+            resultsDB.write("'" + main.commit + "',")
+            resultsDB.write(str(main.numCtrls) + ",")
+            resultsDB.write(str(batchSize) + ",")
+            resultsDB.write(str(aveLatency) + ",")
+            resultsDB.write(str(stdLatency) + "\n")
             resultsDB.close()
-
-            main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"])
-
+        del main.scale[0]
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.params b/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.params
index 3799982..35fbefb 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.params
+++ b/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.params
@@ -1,6 +1,6 @@
 <PARAMS>
 
-    <testcases>1,2,1,2,1,2,1,2</testcases>
+    <testcases>0,1,2,1,2,1,2,1,2</testcases>
 
     <SCALE>1,3,5,7</SCALE>
     <max>7</max>
@@ -14,30 +14,33 @@
         <skipCleanInstall>yes</skipCleanInstall>
         <warmUp>5</warmUp>
         <sampleSize>20</sampleSize>
-        <wait></wait>
         <intents>1,100,1000</intents>                       #list format, will be split on ','
-        <debug>True</debug>
-
-        <s1>1,1,1,1,1,1,1,1</s1>
-        <s3>2,2,1,1,3,3,3,1</s3>
-        <s5>2,2,1,1,3,4,5,3</s5>
-        <s7>2,3,1,1,5,6,7,4</s7>
-
+        <ingress>null:0000000000000001/0</ingress>
+        <egress>null:0000000000000007/0</egress>
+        <debug>False</debug>
     </TEST>
-
-    <METRICS>
-        <Submitted>0</Submitted>
-        <Installed>1</Installed>
-        <Failed>0</Failed>
-        <Withdraw>0</Withdraw>
-        <Withdrawn>0</Withdrawn>
-    </METRICS>
+    <DATABASE>
+        <file>/tmp/IntentRerouteLatDB</file>
+    </DATABASE>
 
     <GIT>
-        <autopull>off</autopull>
-        <checkout>master</checkout>
+        <gitPull>False</gitPull>
+        <gitBranch>master</gitBranch>
     </GIT>
 
+    <ATTEMPTS>
+        <verify>3</verify>
+    </ATTEMPTS>
+
+    <SLEEP>
+        <startup>10</startup>
+        <install>10</install>
+        <verify>5</verify>
+        <reroute>3</reroute>
+        # timeout for pexpect
+        <timeout>300</timeout>
+    </SLEEP>
+
     <CTRL>
         <USER>sdn</USER>
 
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.py b/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.py
index 439ecef..ce9155d 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.py
+++ b/TestON/tests/SCPF/SCPFintentRerouteLatWithFlowObj/SCPFintentRerouteLatWithFlowObj.py
@@ -1,419 +1,369 @@
-# ScaleOutTemplate
-#
-# CASE1 starts number of nodes specified in param file
-#
-# cameron@onlab.us
-
-import sys
-import os.path
-
+# SCPFintentRerouteLatWithFlowObj
+"""
+SCPFintentRerouteLat
+    - Test Intent Reroute Latency
+    - Test Algorithm:
+        1. Start Null Provider reroute Topology
+        2. Using Push-test-intents to push batch size intents from switch 1 to switch 7
+        3. Cut the link between switch 3 and switch 4 (the path will reroute to switch 8)
+        4. Get the topology time stamp
+        5. Get Intent reroute(Installed) time stamp from each nodes
+        6. Use the latest intent time stamp subtract topology time stamp
+    - This test will run 5 warm up by default, warm up iteration can be setup in Param file
+    - The intent batch size will default set to 1, 100, and 1000, also can be set in Param file
+    - The unit of the latency result is milliseconds
+    - Ues flowObject set to True
+"""
 
 class SCPFintentRerouteLatWithFlowObj:
+    def __init__(self):
+        self.default=''
 
-    def __init__( self ):
-        self.default = ''
+    def CASE0( self, main ):
+        '''
+        - GIT
+        - BUILDING ONOS
+            Pull specific ONOS branch, then Build ONOS ono ONOS Bench.
+            This step is usually skipped. Because in a Jenkins driven automated
+            test env. We want Jenkins jobs to pull&build for flexibility to handle
+            different versions of ONOS.
+        - Construct tests variables
+        '''
+        gitPull = main.params['GIT']['gitPull']
+        gitBranch = main.params['GIT']['gitBranch']
+
+        main.case("Pull onos branch and build onos on Teststation.")
+
+        if gitPull == 'True':
+            main.step("Git Checkout ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.gitCheckout(branch=gitBranch)
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully checkout onos branch.",
+                                    onfail="Failed to checkout onos branch. Exiting test...")
+            if not stepResult: main.exit()
+
+            main.step("Git Pull on ONOS branch:" + gitBranch)
+            stepResult = main.ONOSbench.gitPull()
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully pull onos. ",
+                                    onfail="Failed to pull onos. Exiting test ...")
+            if not stepResult: main.exit()
+
+            main.step("Building ONOS branch: " + gitBranch)
+            stepResult = main.ONOSbench.cleanInstall(skipTest=True)
+            utilities.assert_equals(expect=main.TRUE,
+                                    actual=stepResult,
+                                    onpass="Successfully build onos.",
+                                    onfail="Failed to build onos. Exiting test...")
+            if not stepResult: main.exit()
+
+        else:
+            main.log.warn("Skipped pulling onos and Skipped building ONOS")
+
+        main.apps = main.params['ENV']['cellApps']
+        main.BENCHUser = main.params['BENCH']['user']
+        main.BENCHIp = main.params['BENCH']['ip1']
+        main.MN1Ip = main.params['MN']['ip1']
+        main.maxNodes = int(main.params['max'])
+        main.skipMvn = main.params['TEST']['skipCleanInstall']
+        main.cellName = main.params['ENV']['cellName']
+        main.scale = (main.params['SCALE']).split(",")
+        main.dbFileName = main.params['DATABASE']['file']
+        main.timeout = int(main.params['SLEEP']['timeout'])
+        main.startUpSleep = int(main.params['SLEEP']['startup'])
+        main.installSleep = int(main.params['SLEEP']['install'])
+        main.verifySleep = int(main.params['SLEEP']['verify'])
+        main.verifyAttempts = int(main.params['ATTEMPTS']['verify'])
+        main.sampleSize = int(main.params['TEST']['sampleSize'])
+        main.warmUp = int(main.params['TEST']['warmUp'])
+        main.intentsList = (main.params['TEST']['intents']).split(",")
+        main.ingress = main.params['TEST']['ingress']
+        main.egress = main.params['TEST']['egress']
+        main.debug = main.params['TEST']['debug']
+        for i in range ( 0, len(main.intentsList) ):
+            main.intentsList[i] = int(main.intentsList[i])
+         # Create DataBase file
+        main.log.info( "Create Database file " + main.dbFileName )
+        resultsDB = open( main.dbFileName, "w+")
+        resultsDB.close()
+
 
     def CASE1( self, main ):
-
+        '''
+            clean up test environment and set up
+        '''
         import time
 
-        global init
-        try:
-            if type(init) is not bool:
-                init = False
-        except NameError:
-            init = False
+        main.log.info( "Get ONOS cluster IP" )
+        print(main.scale)
+        main.numCtrls = int(main.scale[0])
+        main.ONOSip = []
+        main.maxNumBatch = 0
+        main.AllONOSip = main.ONOSbench.getOnosIps()
+        for i in range(main.numCtrls):
+            main.ONOSip.append(main.AllONOSip[i])
+        main.log.info(main.ONOSip)
+        main.CLIs=[]
+        main.log.info("Creating list of ONOS cli handles")
+        for i in range(main.numCtrls):
+            main.CLIs.append(getattr(main, 'ONOS%scli' % (i + 1)))
 
-        #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' ]
-        BENCHUser = main.params[ 'BENCH' ][ 'user' ]
-        BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
-        MN1Ip = main.params[ 'MN' ][ 'ip1' ]
-        main.maxNodes = int(main.params[ 'max' ])
-        skipMvn = main.params[ 'TEST' ][ 'skipCleanInstall' ]
-        cellName = main.params[ 'ENV' ][ 'cellName' ]
+        if not main.CLIs:
+            main.log.error("Failed to create the list of ONOS cli handles")
+            main.cleanup()
+            main.exit()
 
-        # -- 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
+        main.commit = main.ONOSbench.getVersion(report=True)
+        main.commit = main.commit.split(" ")[1]
+        main.log.info("Starting up %s node(s) ONOS cluster" % main.numCtrls)
+        main.log.info("Safety check, killing all ONOS processes" +
+                      " before initiating environment setup")
 
-            clusterCount = 0
-            ONOSIp = [ 0 ]
-            scale = (main.params[ 'SCALE' ]).split(",")
-            clusterCount = int(scale[0])
+        for i in range(main.numCtrls):
+            main.ONOSbench.onosDie(main.ONOSip[i])
 
-            #Populate ONOSIp with ips from params
-            ONOSIp = [0]
-            ONOSIp.extend(main.ONOSbench.getOnosIps())
+        main.log.info("NODE COUNT = %s" % main.numCtrls)
 
-            print("-----------------" + str(ONOSIp))
-            #mvn clean install, for debugging set param 'skipCleanInstall' to yes to speed up test
-            if skipMvn != "yes":
-                mvnResult = main.ONOSbench.cleanInstall()
+        #tempOnosIp = []
+        #for i in range(main.numCtrls):
+        #    tempOnosIp.append(main.AllONOSip[i])
+        #print(tempOnosIp)
+        main.ONOSbench.createCellFile(main.ONOSbench.ip_address,
+                                      main.cellName,
+                                      main.MN1Ip,
+                                      main.apps,
+                                      main.ONOSip )
+        main.step("Apply cell to environment")
+        cellResult = main.ONOSbench.setCell(main.cellName)
+        verifyResult = main.ONOSbench.verifyCell()
+        stepResult = cellResult and verifyResult
+        utilities.assert_equals(expect=main.TRUE,
+                                actual=stepResult,
+                                onpass="Successfully applied cell to " + \
+                                       "environment",
+                                onfail="Failed to apply cell to environment ")
 
-            #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" )
-
-            commit = main.ONOSbench.getVersion()
-            commit = (commit.split(" "))[1]
-
-            resultsDB = open("/tmp/IntentRerouteLatDBWithFlowObj", "w+")
-            resultsDB.close()
-
-        # -- END OF INIT SECTION --#
-
-        clusterCount = int(scale[0])
-        scale.remove(scale[0])
-
-        #kill off all onos processes
-        main.log.step("Safety check, killing all ONOS processes")
-        main.log.step("before initiating environment setup")
-        for node in range(1, main.maxNodes + 1):
-            main.ONOSbench.onosDie(ONOSIp[node])
-
-        #Uninstall everywhere
-        main.log.step( "Cleaning Enviornment..." )
-        for i in range(1, main.maxNodes + 1):
-            main.log.info(" Uninstalling ONOS " + str(i) )
-            main.ONOSbench.onosUninstall( ONOSIp[i] )
-
-        #construct the cell file
-        main.log.info("Creating cell file")
-        cellIp = []
-        for node in range (1, clusterCount + 1):
-            cellIp.append(ONOSIp[node])
-
-        main.ONOSbench.createCellFile(BENCHIp,cellName,MN1Ip,str(Apps), cellIp)
-
-        main.step( "Set Cell" )
-        main.ONOSbench.setCell(cellName)
-
-        main.step( "Creating ONOS package" )
+        main.step("Creating ONOS package")
         packageResult = main.ONOSbench.onosPackage()
+        stepResult = packageResult
+        utilities.assert_equals(expect=main.TRUE,
+                                actual=stepResult,
+                                onpass="Successfully created ONOS package",
+                                onfail="Failed to create ONOS package")
 
-        main.step( "verify cells" )
-        verifyCellResult = main.ONOSbench.verifyCell()
+        main.step("Uninstall ONOS package on all Nodes")
+        uninstallResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("Uninstalling package on ONOS Node IP: " + main.ONOSip[i])
+            u_result = main.ONOSbench.onosUninstall(main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=u_result,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            uninstallResult = (uninstallResult and u_result)
 
-        main.log.report( "Initializing " + str( clusterCount ) + " node cluster." )
-        for node in range(1, clusterCount + 1):
-            main.log.info("Starting ONOS " + str(node) + " at IP: " + ONOSIp[node])
-            main.ONOSbench.onosInstall( ONOSIp[node])
+        main.step("Install ONOS package on all Nodes")
+        installResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("Installing package on ONOS Node IP: " + main.ONOSip[i])
+            i_result = main.ONOSbench.onosInstall(node=main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=i_result,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            installResult = installResult and i_result
 
-        for node in range(1, clusterCount + 1):
-            for i in range( 2 ):
-                isup = main.ONOSbench.isup( ONOSIp[node] )
-                if isup:
-                    main.log.info("ONOS " + str(node) + " is up\n")
-                    break
-            if not isup:
-                main.log.report( "ONOS " + str(node) + " didn't start!" )
-        main.log.info("Startup sequence complete")
+        main.step("Verify ONOS nodes UP status")
+        statusResult = main.TRUE
+        for i in range(int(main.numCtrls)):
+            main.log.info("ONOS Node " + main.ONOSip[i] + " status:")
+            onos_status = main.ONOSbench.onosStatus(node=main.ONOSip[i])
+            utilities.assert_equals(expect=main.TRUE, actual=onos_status,
+                                    onpass="Test step PASS",
+                                    onfail="Test step FAIL")
+            statusResult = (statusResult and onos_status)
+        time.sleep(2)
+        main.step("Start ONOS CLI on all nodes")
+        cliResult = main.TRUE
+        main.log.step(" Start ONOS cli using thread ")
+        startCliResult = main.TRUE
+        pool = []
+        main.threadID=0
+        for i in range(int(main.numCtrls)):
+            t = main.Thread(target=main.CLIs[i].startOnosCli,
+                            threadID=main.threadID,
+                            name="startOnosCli",
+                            args=[main.ONOSip[i]],
+                            kwargs={"onosStartTimeout": main.timeout})
+            pool.append(t)
+            t.start()
+            main.threadID = main.threadID + 1
+        for t in pool:
+            t.join()
+            startCliResult = startCliResult and t.result
+        time.sleep(main.startUpSleep)
 
-        deviceMastership = (main.params[ 'TEST' ][ "s" + str(clusterCount) ]).split(",")
-        print("Device mastership list: " + str(deviceMastership))
+        # configure apps
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "deviceCount", value=8)
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "topoShape", value="reroute")
+        main.CLIs[0].setCfg("org.onosproject.provider.nil.NullProviders", "enabled", value="true")
+        main.CLIs[0].setCfg("org.onosproject.store.flow.impl.DistributedFlowRuleStore", "backupEnabled", value="false")
+        main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
+                            "useFlowObjectives", value="true")
+        time.sleep(main.startUpSleep)
 
-        main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.store.flow.impl.NewDistributedFlowRuleStore", "backupEnabled false")
+        # Balance Master
+        main.CLIs[0].balanceMasters()
+        if len(main.ONOSip) > 1:
+            main.CLIs[0].deviceRole("null:0000000000000003", main.ONOSip[0])
+            main.CLIs[0].deviceRole("null:0000000000000004", main.ONOSip[0])
 
-        main.log.step("Setting up null provider")
-        for i in range(3):
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "deviceCount 8")
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "topoShape reroute")
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.provider.nil.NullProviders", "enabled true")
-            main.ONOSbench.onosCfgSet( ONOSIp[1], "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator", "useFlowObjectives true" )
-            time.sleep(5)
-            main.ONOSbench.handle.sendline("onos $OC1 summary")
-            main.ONOSbench.handle.expect(":~")
-            x = main.ONOSbench.handle.before
-            if "devices=8" in x and "links=16," in x:
-                break
-
-        index = 1
-        for node in deviceMastership:
-            for attempt in range(0,10):
-                cmd = ( "onos $OC" + node + """ "device-role null:000000000000000""" + str(index) + " " + ONOSIp[int(node)]  + """ master" """)
-                main.log.info("assigning mastership of device " + str(index) + " to node " + node + ": \n " + cmd + "\n")
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                time.sleep(4)
-
-                cmd = ( "onos $OC" + node + " roles|grep 00000" + str(index))
-                main.log.info(cmd)
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                check = main.ONOSbench.handle.before
-                main.log.info("CHECK:\n" + check)
-                if ("master=" + ONOSIp[int(node)]) in check:
-                    break
-            index += 1
-
-        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"])
-
-    def CASE2( self, main ):
-
+    def CASE2( self, main):
         import time
         import numpy
         import datetime
-        #from scipy import stats
+        import json
+        # from scipy import stats
 
         ts = time.time()
-
-        sampleSize = int(main.params[ 'TEST' ][ 'sampleSize' ])
-        warmUp = int(main.params[ 'TEST' ][ 'warmUp' ])
-        intentsList = (main.params[ 'TEST' ][ 'intents' ]).split(",")
-        debug = main.params[ 'TEST' ][ 'debug' ]
-        for i in range(0,len(intentsList)):
-            intentsList[i] = int(intentsList[i])
-
-        timestampMetrics = []
-        if main.params['METRICS']['Submitted'] == "1":
-            timestampMetrics.append("Submitted")
-        if main.params['METRICS']['Installed'] == "1":
-            timestampMetrics.append("Installed")
-        if main.params['METRICS']['Failed'] == "1":
-            timestampMetrics.append("Failed")
-        if main.params['METRICS']['Withdraw'] == "1":
-            timestampMetrics.append("Withdraw")
-        if main.params['METRICS']['Withdrawn'] == "1":
-            timestampMetrics.append("Withdrawn")
-        if debug: main.log.info(timestampMetrics)
-
-        if debug == "True":
-            debug = True
-        else:
-            debug = False
-
-        ingress = "null:0000000000000001"
-        egress = "null:0000000000000007"
-
-        for intents in intentsList:
-            main.log.report("Intent Batch size: " + str(intents) + "\n      ")
-            myResult = [["latency", "lastNode"] for x in range(sampleSize)]
-
-            for run in range(0, (warmUp + sampleSize)):
-                if run > warmUp:
-                    main.log.info("Starting test iteration " + str(run-warmUp))
-
-                cmd = """onos $OC1 "push-test-intents -i """
-                cmd += ingress + "/0 "
-                cmd += egress + "/0 "
-                cmd += str(intents) +""" 1" """
-                if debug: main.log.info(cmd)
-
-                withdrawCmd = cmd.replace("intents -i", "intents -w ")
-
-                #push-test-intents
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-                myRawResult = main.ONOSbench.handle.before
-
-                for i in range(0, 40):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    if ("links=16,") in linkCheck and ("flows=" + str(intents*7) + ","):
-                        break
-                    if i == 39:
-                        main.log.error("Flow/link count incorrect, data invalid."+ linkCheck)
-                        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"], "d")
-                        #main.ONOSbench.logReport(ONOSIp[(clusterCount-1)], ["ERROR", "WARNING", "EXCEPT"], "d")
-                        main.ONOSbench.sendline("onos $OC1 summary")
-                        main.ONOSbench.sendline("onos $OC1 devices")
-                        main.ONOSbench.sendline("onos $OC1 links")
-                        main.ONOSbench.expect(":~")
-                        main.log.info(main.ONOSbench.before)
-
-                #collect timestamp from link cut
-                cmd = """onos $OC1 null-link "null:0000000000000004/1 null:0000000000000003/2 down" """
-                if debug: main.log.info("COMMAND: " + str(cmd))
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-
-                cmd = "onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep TopologyManager| tail -1"
-                for i in range(0,10):
-                    main.ONOSbench.handle.sendline(cmd)
-                    time.sleep(2)
-                    main.ONOSbench.handle.expect(":~")
-                    raw = main.ONOSbench.handle.before
-                    #if "NullLinkProvider" in raw and "links=14" in raw:
-                    if "links=14" in raw:
-                        break
-                    if i >= 9:
-                        main.log.error("Expected output not being recieved... continuing")
-                        main.log.info(raw)
-                        break
-                    time.sleep(2)
-
-                if debug: main.log.debug("raw: " + raw)
-
-                temp = raw.splitlines()
-
-                if debug: main.log.debug("temp (after splitlines): " + str(temp))
-
-                # Since the string is deterministic the date is always the 3rd element.
-                # However, if the data were grepping for in the onos log changes then this will
-                # not work. This is why we print out the raw and temp string so we can visually
-                # check if everything is in the correct order. temp should like this:
-                # temp = ['/onos$ onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep Top ', 
-                #         'ologyManager| tail -1', '2015-10-15 12:03:33,736 ... ]
-                temp = temp[2]
-
-                if debug: main.log.debug("temp (checking for date): " + str(temp))
-
-                cutTimestamp = (temp.split(" "))[0] + " " + (temp.split(" "))[1]
-
-                if debug: main.log.info("Cut timestamp: " + cutTimestamp)
-
-                #validate link count and flow count
-                for i in range(0, 40):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    #if "links=" + str(7*intents)+ "," in linkCheck and ("flows=" + str(7*intents) + ",") in linkCheck:
-                    if "links=14," in linkCheck and ("flows=" + str(8*intents) + ",") in linkCheck:
-                        break
-                    if i == 39:
-                        main.log.error("Link or flow count incorrect, data invalid." + linkCheck)
-                        main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"], "d")
-
-                time.sleep(5) #trying to avoid negative values
-
-                #intents events metrics installed timestamp
-                IEMtimestamps = [0]*(clusterCount + 1)
-                installedTemp = [0]*(clusterCount + 1)
-                for node in range(1, clusterCount +1):
-                    cmd = "onos $OC" + str(node) + """ "intents-events-metrics"|grep Timestamp """
-                    raw = ""
-                    while "epoch)" not in raw:
-                        main.ONOSbench.handle.sendline(cmd)
-                        main.ONOSbench.handle.expect(":~")
-                        raw = main.ONOSbench.handle.before
-
-                    print(raw)
-
-                    intentsTimestamps = {}
-                    rawTimestamps = raw.splitlines()
-                    for line in rawTimestamps:
-                        if "Timestamp" in line and "grep" not in line:
-                            metricKey = (line.split(" "))[1]
-                            metricTimestamp = (line.split(" ")[len(line.split(" ")) -1]).replace("epoch)=","")
-                            metricTimestamp = float(metricTimestamp)
-                            metricTimestamp = numpy.divide(metricTimestamp, 1000)
-                            if debug: main.log.info(repr(metricTimestamp))
-                            intentsTimestamps[metricKey] = metricTimestamp
-                            if metricKey == "Installed":
-                                installedTemp[node] = metricTimestamp
-
-                    main.log.info("Node: " + str(node) + " Timestamps: " + str(intentsTimestamps))
-                    IEMtimestamps[node] = intentsTimestamps
-
-                myMax = max(installedTemp)
-                indexOfMax = installedTemp.index(myMax)
-
-                #number crunch
-                for metric in timestampMetrics:     #this is where we sould add support for computing other timestamp metrics
-                    if metric == "Installed":
-                        if run >= warmUp:
-                            main.log.report("link cut timestamp: " + cutTimestamp)
-                            #readableInstalledTimestamp = str(intentsTimestamps["Installed"])
-                            readableInstalledTimestamp = str(myMax)
-
-                            #main.log.report("Intent Installed timestamp: " + str(intentsTimestamps["Installed"]))
-                            main.log.report("Intent Installed timestamp: " + str(myMax))
-
-                            cutEpoch = time.mktime(time.strptime(cutTimestamp, "%Y-%m-%d %H:%M:%S,%f"))
-                            if debug: main.log.info("cutEpoch=" + str(cutEpoch))
-                            #rerouteLatency = float(intentsTimestamps["Installed"] - cutEpoch)
-                            rerouteLatency = float(myMax - cutEpoch)
-
-                            rerouteLatency = numpy.divide(rerouteLatency, 1000)
-                            main.log.report("Reroute latency:" + str(rerouteLatency) + " (seconds)\n    ")
-                            myResult[run-warmUp][0] = rerouteLatency
-                            myResult[run-warmUp][1] = indexOfMax
-                            if debug: main.log.info("Latency: " + str(myResult[run-warmUp][0]))
-                            if debug: main.log.info("last node: " + str(myResult[run-warmUp][1]))
-
-                cmd = """ onos $OC1 null-link "null:0000000000000004/1 null:0000000000000003/2 up" """
-                if debug: main.log.info(cmd)
-                main.ONOSbench.handle.sendline(cmd)
-                main.ONOSbench.handle.expect(":~")
-
-                #wait for intent withdraw
-                main.ONOSbench.handle.sendline(withdrawCmd)
-                main.log.info(withdrawCmd)
-                main.ONOSbench.handle.expect(":~")
-                if debug: main.log.info(main.ONOSbench.handle.before)
-                main.ONOSbench.handle.sendline("onos $OC1 intents|grep WITHDRAWN|wc -l")
-                main.ONOSbench.handle.expect(":~")
-                intentWithdrawCheck = main.ONOSbench.handle.before
-                if (str(intents)) in intentWithdrawCheck:
-                    main.log.info("intents withdrawn")
-                if debug: main.log.info(intentWithdrawCheck)
-
-                # wait for links to be reestablished
-                for i in range(0, 10):
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
-                    linkCheck = main.ONOSbench.handle.before
-                    if "links=16," in linkCheck:
-                        break
-                    time.sleep(1)
-                    if i == 9:
-                        main.log.info("Links Failed to reconnect, next iteration of data invalid." + linkCheck)
-
-                if run < warmUp:
-                    main.log.info("Warm up run " + str(run+1) + " completed")
-
-            if debug: main.log.info(myResult)
-            latTemp = []
-            nodeTemp = []
-            for i in myResult:
-                latTemp.append(i[0])
-                nodeTemp.append(i[1])
-
-            mode = {}
-            for i in nodeTemp:
-                if i in mode:
-                    mode[i] += 1
+        print( main.intentsList )
+        for batchSize in main.intentsList:
+            main.log.report("Intent Batch size: " + str(batchSize) + "\n      ")
+            main.LatencyList = []
+            for run in range( 0, (main.warmUp + main.sampleSize) ):
+                if run >= main.warmUp:
+                    main.log.info( "================================================" )
+                    main.log.info( "Starting test iteration " + str(run - main.warmUp) )
+                    main.log.info( "================================================" )
                 else:
-                    mode[i] = 1
+                    main.log.info( "====================Warm Up=====================" )
 
-            for i in mode.keys():
-                if mode[i] == max(mode.values()):
-                    nodeMode = i
+                # push intents
+                main.CLIs[0].pushTestIntents(main.ingress, main.egress, batchSize,
+                                             offset=1,options="-i",timeout=main.timeout)
 
-            average = numpy.average(latTemp)
-            stdDev = numpy.std(latTemp)
+                # check links and flows
+                k = 0
+                verify = main.FALSE
+                linkCheck = 0
+                flowsCheck = 0
+                while k <= main.verifyAttempts:
+                    time.sleep( main.verifySleep )
+                    summary = json.loads( main.CLIs[0].summary(timeout=main.timeout) )
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    if linkCheck == 16 and flowsCheck == batchSize*7:
+                        main.log.info( "links: {}, flows: {} ".format(linkCheck,flowsCheck) )
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.warn( "Links or flows number are not match!")
+                    main.log.warn( "links: {}, flows: {} ".format(linkCheck, flowsCheck) )
+                    continue
 
-            average = numpy.multiply(average, 1000)
-            stdDev = numpy.multiply(stdDev, 1000)
+                # Bring link down
+                main.CLIs[0].link("0000000000000004/1", "0000000000000003/2", "down",
+                                  timeout=main.timeout, showResponse=False )
+                verify = main.FALSE
+                k = 0
+                topoManagerLog = ""
+                while k <= main.verifyAttempts:
+                    time.sleep(main.verifySleep)
+                    summary = json.loads( main.CLIs[0].summary(timeout=main.timeout) )
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    if linkCheck == 14:
+                        main.log.info( "links: {}, flows: {} ".format(linkCheck, flowsCheck) )
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.warn( "Links number are not match in TopologyManager log!" )
+                    main.log.warn( topoManagerLog )
+                    continue
 
-            main.log.report("Scale: " + str(clusterCount) + "  \tIntent batch: " + str(intents))
-            main.log.report("Latency average:................" + str(average))
-            main.log.report("Latency standard deviation:....." + str(stdDev))
-            main.log.report("Mode of last node to respond:..." + str(nodeMode))
+                try:
+                    # expect twice to clean the pexpect buffer
+                    main.ONOSbench.handle.sendline("")
+                    main.ONOSbench.handle.expect("\$")
+                    main.ONOSbench.handle.expect("\$")
+                    # send line by using bench, can't use driver because pexpect buffer problem
+                    cmd = "onos-ssh $OC1 cat /opt/onos/log/karaf.log | grep TopologyManager| tail -1"
+                    main.ONOSbench.handle.sendline(cmd)
+                    time.sleep(1)
+                    main.ONOSbench.handle.expect(":~")
+                    topoManagerLog = main.ONOSbench.handle.before
+                    topoManagerLogTemp = topoManagerLog.splitlines()
+                    # To make sure we get correct topology log
+                    for lines in topoManagerLogTemp:
+                        if "creationTime" in lines:
+                            topoManagerLog = lines
+                    main.log.info("Topology Manager log:")
+                    print(topoManagerLog)
+                    cutTimestamp = float(topoManagerLog.split("creationTime=")[1].split(",")[0])
+                except:
+                    main.log.error("Topology Log is not correct!")
+                    print(topoManagerLog)
+                    # If we got wrong Topology log, we should skip this iteration, and continue for next one
+                    continue
+
+                installedTemp = []
+                time.sleep(1)
+                for cli in main.CLIs:
+                    tempJson = json.loads( cli.intentsEventsMetrics() )
+                    Installedtime = tempJson.get('intentInstalledTimestamp').get('value')
+                    installedTemp.append(float(Installedtime))
+                for i in range(0, len(installedTemp)):
+                    main.log.info("ONOS Node {} Installed Time stemp: {}".format((i+1),installedTemp[i]))
+                maxInstallTime = float( max(installedTemp) )
+                if run >= main.warmUp:
+                    main.log.info( "Installed time stemp: {0:f}".format( maxInstallTime ) )
+                    main.log.info("CutTimestamp: {0:f}".format( cutTimestamp) )
+                    # Both timeStemps are milliseconds
+                    main.log.info( "Latency: {0:f}".format( float(maxInstallTime-cutTimestamp)) )
+                    main.LatencyList.append(float(maxInstallTime-cutTimestamp))
+
+                # Verify Summary after we bring up link, and withdrawn intents
+                main.CLIs[0].link( "0000000000000004/1", "0000000000000003/2", "up",
+                                   timeout=main.timeout )
+                k = 0
+                verify = main.FALSE
+                linkCheck = 0
+                flowsCheck = 0
+                while k <= main.verifyAttempts:
+                    time.sleep(main.verifySleep)
+                    main.CLIs[0].removeAllIntents( purge=True, sync=True, timeout=main.timeout )
+                    time.sleep(1)
+                    main.CLIs[0].purgeWithdrawnIntents()
+                    summary = json.loads( main.CLIs[0].summary() )
+                    linkCheck = summary.get("links")
+                    flowsCheck = summary.get("flows")
+                    intentCheck = summary.get("intents")
+                    if linkCheck == 16 and flowsCheck == 0 and intentCheck == 0:
+                        main.log.info("links: {}, flows: {}, intents: {} ".format(linkCheck, flowsCheck, intentCheck))
+                        verify = main.TRUE
+                        break
+                    k += 1
+                if not verify:
+                    main.log.error("links, flows, or intents are not correct!")
+                    main.log.info("links: {}, flows: {}, intents: {} ".format(linkCheck, flowsCheck, intentCheck))
+                    continue
+
+            aveLatency=0
+            stdLatency=0
+            aveLatency = numpy.average(main.LatencyList)
+            stdLatency = numpy.std(main.LatencyList)
+            main.log.report("Scale: " + str(main.numCtrls) + "  \tIntent batch: " + str(batchSize))
+            main.log.report("Latency average:................" + str(aveLatency))
+            main.log.report("Latency standard deviation:....." + str(stdLatency))
             main.log.report("________________________________________________________")
 
-            resultsDB = open("/tmp/IntentRerouteLatDBWithFlowObj", "a")
-            resultsDB.write("'" + commit + "',")
-            resultsDB.write(str(clusterCount) + ",")
-            resultsDB.write(str(intents) + ",")
-            resultsDB.write(str(average) + ",")
-            resultsDB.write(str(stdDev) + "\n")
+            resultsDB = open(main.dbFileName, "a")
+            resultsDB.write("'" + main.commit + "',")
+            resultsDB.write(str(main.numCtrls) + ",")
+            resultsDB.write(str(batchSize) + ",")
+            resultsDB.write(str(aveLatency) + ",")
+            resultsDB.write(str(stdLatency) + "\n")
             resultsDB.close()
-
-            main.ONOSbench.logReport(ONOSIp[1], ["ERROR", "WARNING", "EXCEPT"])
+        del main.scale[0]
diff --git a/TestON/tests/SCPF/SCPFmaxIntents/Dependency/maxIntentFunctions.py b/TestON/tests/SCPF/SCPFmaxIntents/dependencies/maxIntentFunctions.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFmaxIntents/Dependency/maxIntentFunctions.py
rename to TestON/tests/SCPF/SCPFmaxIntents/dependencies/maxIntentFunctions.py
diff --git a/TestON/tests/SCPF/SCPFmaxIntents/Dependency/rerouteTopo.py b/TestON/tests/SCPF/SCPFmaxIntents/dependencies/rerouteTopo.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFmaxIntents/Dependency/rerouteTopo.py
rename to TestON/tests/SCPF/SCPFmaxIntents/dependencies/rerouteTopo.py
diff --git a/TestON/tests/SCPF/SCPFmaxIntents/Dependency/startUp.py b/TestON/tests/SCPF/SCPFmaxIntents/dependencies/startUp.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFmaxIntents/Dependency/startUp.py
rename to TestON/tests/SCPF/SCPFmaxIntents/dependencies/startUp.py
diff --git a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
index f1101fe..4f7bfb7 100644
--- a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
+++ b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
@@ -109,7 +109,7 @@
         threads = []
         for h in hostList:
             main.Mininet1.arping( srcHost=h, dstHost="10.0.0.1", output=main.FALSE, noResult=True )
-            time.sleep(1)
+            time.sleep(0.5)
     else:
         main.Mininet1.arping(srcHost=hostList)
     summaryStr = json.loads( main.CLIs[0].summary().encode() )
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
index 6efe17e..cb75b01 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
@@ -13,14 +13,14 @@
     <SCALE>1,3,5,7</SCALE>
 
     <DEPENDENCY>
-        <path>/tests/SCPFscalingMaxIntents/dependencies/</path>
+        <path>/tests/SCPF/SCPFscalingMaxIntents/dependencies/</path>
         <wrapper1>startUp</wrapper1>
         <topology>rerouteTopo.py</topology>
     </DEPENDENCY>
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers</cellApps>
+        <cellApps>drivers,openflow</cellApps>
     </ENV>
 
     <GIT>
@@ -61,10 +61,10 @@
     <NULL>
         # CASE20
         <PUSH>
-            <batch_size>100</batch_size>
-            <min_intents>100</min_intents>
-            <max_intents>100000</max_intents>
-            <check_interval>100</check_interval>
+            <batch_size>1000</batch_size>
+            <min_intents>10000</min_intents>
+            <max_intents>70000</max_intents>
+            <check_interval>10000</check_interval>
         </PUSH>
 
         # if reroute is true
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
index 27bc5c2..a6f4445 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
@@ -54,10 +54,6 @@
             main.reroute = False
 
         main.CLIs = []
-        main.ONOSip = []
-        main.maxNumBatch = 0
-        main.ONOSip = main.ONOSbench.getOnosIps()
-        main.log.info(main.ONOSip)
         main.setupSkipped = False
 
         wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
@@ -82,6 +78,13 @@
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.CLIs = []
         main.numCtrls = int( main.scale[ 0 ] )
+        main.ONOSip = []
+        main.maxNumBatch = 0
+        main.AllONOSip = main.ONOSbench.getOnosIps()
+        for i in range(main.numCtrls):
+            main.ONOSip.append(main.AllONOSip[i])
+        main.log.info(main.ONOSip)
+
         main.log.info( "Creating list of ONOS cli handles" )
         for i in range(main.numCtrls):
             main.CLIs.append( getattr( main, 'ONOScli%s' % (i+1) ) )
@@ -302,19 +305,11 @@
         '''
         import json
         import time
-
-        time.sleep(main.startUpSleep)
-
-        main.step("Activating openflow")
-        appStatus = utilities.retry( main.ONOSrest1.activateApp,
-                                     main.FALSE,
-                                     ['org.onosproject.openflow'],
-                                     sleep=3,
-                                     attempts=3 )
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=appStatus,
-                                 onpass="Successfully activated openflow",
-                                 onfail="Failed activate openflow" )
+ 
+        devices = []
+        devices = main.CLIs[0].getAllDevicesId()
+        for d in devices:
+            main.CLIs[0].deviceRemove( d )
 
         time.sleep(main.startUpSleep)
         main.step('Starting mininet topology')
@@ -333,13 +328,26 @@
                                  onfail="Failed assign switches to masters" )
 
         time.sleep(main.startUpSleep)
+        # Balancing Masters
+        main.step( "Balancing Masters" )
+        stepResult = main.FALSE
+        stepResult = utilities.retry( main.CLIs[0].balanceMasters,
+                                      main.FALSE,
+                                      [],
+                                      sleep=3,
+                                      attempts=3 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                       actual=stepResult,
+                                       onpass="Balance masters was successfull",
+                                       onfail="Failed to balance masters" )
 
         main.log.info("Getting default flows")
         jsonSum = json.loads(main.CLIs[0].summary())
         main.defaultFlows = jsonSum["flows"]
 
         main.step("Check status of Mininet setup")
-        caseResult = appStatus and mnStatus and swStatus
+        caseResult = mnStatus and swStatus
         utilities.assert_equals( expect=main.TRUE,
                                  actual=caseResult,
                                  onpass="Successfully setup Mininet",
@@ -393,6 +401,7 @@
         # keeps track of how many flows have been installed, set to 0 at start
         currFlows = 0
         # limit for the number of intents that can be installed
+        main.batchSize = int( int(main.batchSize)/int(main.numCtrls))
         limit = main.maxIntents / main.batchSize
         # total intents installed
         totalIntents = 0
@@ -404,7 +413,14 @@
         stepResult = main.TRUE
         # temp variable to contain the number of flows
         flowsNum = 0
+        if main.numCtrls > 1:
+            # if more than one onos nodes, we should check more frequently
+            main.checkInterval = main.checkInterval/4
 
+        # make sure the checkInterval divisible batchSize
+        main.checkInterval = int( int( main.checkInterval / main.batchSize ) * main.batchSize )
+        flowTemp=0
+        totalFlows=0
         for i in range(limit):
 
             # Threads pool
@@ -424,7 +440,8 @@
                                  kwargs={ "offset": offtmp,
                                           "options": "-i",
                                           "timeout": main.timeout,
-                                          "background":False } )
+                                          "background":False,
+                                          "noExit":True} )
                 pool.append(t)
                 t.start()
                 main.threadID = main.threadID + 1
@@ -441,115 +458,53 @@
                 main.log.info("Verify Intents states")
                 # k is a control variable for verify retry attempts
                 k = 1
-                intentVerify = main.FALSE
 
                 while k <= main.verifyAttempts:
                     # while loop for check intents by using REST api
                     time.sleep(5)
                     temp = 0
-                    intentsState = json.loads( main.ONOSrest1.intents() )
-                    for f in intentsState:
-                        # get INSTALLED intents number
-                        if f.get("state") == "INSTALLED":
-                            temp = temp + 1
-
-                    main.log.info("Total Intents: {} INSTALLED: {}".format(totalIntents, temp))
-                    if totalIntents == temp:
-                        intentVerify = main.TRUE
+                    intentsState = main.CLIs[0].checkIntentSummary(timeout=600)
+                    if intentsState:
+                        verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                        if temp < verifyTotalIntents:
+                            temp = verifyTotalIntents 
+                        else:
+                            verifytotalIntents = temp
+                        main.log.info("Total Intents: {}".format( verifyTotalIntents ) )
                         break
-                    intentVerify = main.FALSE
                     k = k+1
-                if not intentVerify:
+
+                totalFlows = main.CLIs[0].getTotalFlowsNum( timeout=600, noExit=True )
+                if flowTemp < totalFlows:
+                    flowTemp = totalFlows
+                else:
+                    totalFlows = flowTemp 
+
+                if not intentsState:
                     # If some intents are not installed, grep the previous flows list, and finished this test case
                     main.log.warn( "Some intens did not install" )
-                    # We don't want to check flows if intents not installed, because onos will drop flows
-                    if currFlows == 0:
-                    # If currFlows equal 0, which means we failed to install intents at first, or we didn't get
-                    # the correct number, so we need get flows here.
-                        flowsState = json.loads( main.ONOSrest1.flows() )
+                    verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                    main.log.info("Total Intents: {}".format( verifyTotalIntents) )
                     break
 
-                main.log.info("Verify Flows states")
-                k = 1
-                flowsVerify = main.TRUE
-                while k <= main.verifyAttempts:
-                    # while loop for check flows by using REST api
-                    time.sleep(3)
-                    temp = 0
-                    flowsStateCount = []
-                    flowsState = json.loads( main.ONOSrest1.flows() )
-                    main.log.info("Total flows now: {}".format(len(flowsState)))
-                    if ( flowsNum < len(flowsState) ):
-                        flowsNum = len(flowsState)
-                    print(flowsNum)
-                    for f in flowsState:
-                        # get PENDING_ADD flows
-                        if f.get("state") == "PENDING_ADD":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get PENDING_REMOVE flows
-                        if f.get("state") == "PENDING_REMOVE":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get REMOVED flows
-                        if f.get("state") == "REMOVED":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get FAILED flwos
-                        if f.get("state") == "FAILED":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-                    k = k + 1
-                    for c in flowsStateCount:
-                        if int(c) > 0:
-                            flowsVerify = main.FALSE
-
-                    main.log.info( "Check flows States:" )
-                    main.log.info( "PENDING_ADD: {}".format( flowsStateCount[0]) )
-                    main.log.info( "PENDING_REMOVE: {}".format( flowsStateCount[1]) )
-                    main.log.info( "REMOVED: {}".format( flowsStateCount[2]) )
-                    main.log.info( "FAILED: {}".format( flowsStateCount[3]) )
-
-                    if flowsVerify == main.TRUE:
-                        break
-
         del main.scale[0]
         utilities.assert_equals( expect = main.TRUE,
-                                 actual = intentVerify,
+                                 actual = intentsState,
                                  onpass = "Successfully pushed and verified intents",
                                  onfail = "Failed to push and verify intents" )
 
-        # we need the total intents before crash
-        totalIntents = len(intentsState)
-        totalFlows = flowsNum
-
         main.log.info( "Total Intents Installed before crash: {}".format( totalIntents ) )
         main.log.info( "Total Flows ADDED before crash: {}".format( totalFlows ) )
 
         main.step('clean up Mininet')
         main.Mininet1.stopNet()
-
         main.log.info("Writing results to DS file")
         with open(main.dbFileName, "a") as dbFile:
             # Scale number
             temp = str(main.numCtrls)
             temp += ",'" + "baremetal1" + "'"
             # how many intents we installed before crash
-            temp += "," + str(totalIntents)
+            temp += "," + str(verifyTotalIntents)
             # how many flows we installed before crash
             temp += "," + str(totalFlows)
             # other columns in database, but we didn't use in this test
diff --git a/TestON/tests/SAMP/SAMPstartTemplate/__init__.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/__init__.py
similarity index 100%
copy from TestON/tests/SAMP/SAMPstartTemplate/__init__.py
copy to TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/__init__.py
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/Dependency/rerouteTopo.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/rerouteTopo.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFscalingMaxIntents/Dependency/rerouteTopo.py
rename to TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/rerouteTopo.py
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/Dependency/startUp.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/startUp.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFscalingMaxIntents/Dependency/startUp.py
rename to TestON/tests/SCPF/SCPFscalingMaxIntents/dependencies/startUp.py
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.params b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.params
index ed0badf..8083e7a 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.params
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.params
@@ -13,14 +13,14 @@
     <SCALE>1,3,5,7</SCALE>
 
     <DEPENDENCY>
-        <path>/tests/SCPFscalingMaxIntents/dependencies/</path>
+        <path>/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/dependencies/</path>
         <wrapper1>startUp</wrapper1>
         <topology>rerouteTopo.py</topology>
     </DEPENDENCY>
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers</cellApps>
+        <cellApps>drivers,openflow</cellApps>
     </ENV>
 
     <GIT>
@@ -61,10 +61,10 @@
     <NULL>
         # CASE20
         <PUSH>
-            <batch_size>100</batch_size>
-            <min_intents>100</min_intents>
-            <max_intents>100000</max_intents>
-            <check_interval>100</check_interval>
+            <batch_size>1000</batch_size>
+            <min_intents>10000</min_intents>
+            <max_intents>70000</max_intents>
+            <check_interval>10000</check_interval>
         </PUSH>
 
         # if reroute is true
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
index e405fd3..438338a 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
@@ -3,7 +3,7 @@
 import time
 import os
 '''
-SCPFscalingMaxIntentsWithFlowObj
+SCPFscalingMaxIntents
 Push test Intents to onos
 CASE10: set up Null Provider
 CASE11: set up Open Flows
@@ -54,10 +54,6 @@
             main.reroute = False
 
         main.CLIs = []
-        main.ONOSip = []
-        main.maxNumBatch = 0
-        main.ONOSip = main.ONOSbench.getOnosIps()
-        main.log.info(main.ONOSip)
         main.setupSkipped = False
 
         wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
@@ -82,6 +78,13 @@
         # main.scale[ 0 ] determines the current number of ONOS controller
         main.CLIs = []
         main.numCtrls = int( main.scale[ 0 ] )
+        main.ONOSip = []
+        main.maxNumBatch = 0
+        main.AllONOSip = main.ONOSbench.getOnosIps()
+        for i in range(main.numCtrls):
+            main.ONOSip.append(main.AllONOSip[i])
+        main.log.info(main.ONOSip)
+
         main.log.info( "Creating list of ONOS cli handles" )
         for i in range(main.numCtrls):
             main.CLIs.append( getattr( main, 'ONOScli%s' % (i+1) ) )
@@ -301,25 +304,16 @@
             Setting up mininet
         '''
         import json
-        import time
+        import time 
+        devices = []
+        devices = main.CLIs[0].getAllDevicesId()
+        for d in devices:
+            main.CLIs[0].deviceRemove( d )
 
-        time.sleep(main.startUpSleep)
-
-        main.step("Activating openflow")
-        appStatus = utilities.retry( main.ONOSrest1.activateApp,
-                                     main.FALSE,
-                                     ['org.onosproject.openflow'],
-                                     sleep=3,
-                                     attempts=3 )
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=appStatus,
-                                 onpass="Successfully activated openflow",
-                                 onfail="Failed activate openflow" )
-
-        time.sleep(main.startUpSleep)
         main.log.info("Set Intent Compiler use Flow Object")
-        main.CLIs[0].setCfg( "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator", "useFlowObjectives", "true" )
-
+        main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
+        "useFlowObjectives", "true")
+        time.sleep(main.startUpSleep)
         main.step('Starting mininet topology')
         mnStatus = main.Mininet1.startNet(topoFile='~/mininet/custom/rerouteTopo.py')
         utilities.assert_equals( expect=main.TRUE,
@@ -336,13 +330,26 @@
                                  onfail="Failed assign switches to masters" )
 
         time.sleep(main.startUpSleep)
+        # Balancing Masters
+        main.step( "Balancing Masters" )
+        stepResult = main.FALSE
+        stepResult = utilities.retry( main.CLIs[0].balanceMasters,
+                                      main.FALSE,
+                                      [],
+                                      sleep=3,
+                                      attempts=3 )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                       actual=stepResult,
+                                       onpass="Balance masters was successfull",
+                                       onfail="Failed to balance masters" )
 
         main.log.info("Getting default flows")
         jsonSum = json.loads(main.CLIs[0].summary())
         main.defaultFlows = jsonSum["flows"]
 
         main.step("Check status of Mininet setup")
-        caseResult = appStatus and mnStatus and swStatus
+        caseResult = mnStatus and swStatus
         utilities.assert_equals( expect=main.TRUE,
                                  actual=caseResult,
                                  onpass="Successfully setup Mininet",
@@ -396,6 +403,7 @@
         # keeps track of how many flows have been installed, set to 0 at start
         currFlows = 0
         # limit for the number of intents that can be installed
+        main.batchSize = int( int(main.batchSize)/int(main.numCtrls))
         limit = main.maxIntents / main.batchSize
         # total intents installed
         totalIntents = 0
@@ -407,7 +415,15 @@
         stepResult = main.TRUE
         # temp variable to contain the number of flows
         flowsNum = 0
+        if main.numCtrls > 1:
+            # if more than one onos nodes, we should check more frequently
+            main.checkInterval = main.checkInterval/4
 
+        # make sure the checkInterval divisible batchSize
+        main.checkInterval = int( int( main.checkInterval / main.batchSize ) * main.batchSize )
+        flowTemp=0
+        totalFlows=0
+        verifyTotalIntents=0
         for i in range(limit):
 
             # Threads pool
@@ -427,7 +443,8 @@
                                  kwargs={ "offset": offtmp,
                                           "options": "-i",
                                           "timeout": main.timeout,
-                                          "background":False } )
+                                          "background":False,
+                                          "noExit":True} )
                 pool.append(t)
                 t.start()
                 main.threadID = main.threadID + 1
@@ -444,108 +461,46 @@
                 main.log.info("Verify Intents states")
                 # k is a control variable for verify retry attempts
                 k = 1
-                intentVerify = main.FALSE
 
                 while k <= main.verifyAttempts:
                     # while loop for check intents by using REST api
                     time.sleep(5)
                     temp = 0
-                    intentsState = json.loads( main.ONOSrest1.intents() )
-                    for f in intentsState:
-                        # get INSTALLED intents number
-                        if f.get("state") == "INSTALLED":
-                            temp = temp + 1
-
-                    main.log.info("Total Intents: {} INSTALLED: {}".format(totalIntents, temp))
-                    if totalIntents == temp:
-                        intentVerify = main.TRUE
+                    intentsState = main.CLIs[0].checkIntentSummary(timeout=600)
+                    if intentsState:
+                        verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                        if temp < verifyTotalIntents:
+                            temp = verifyTotalIntents
+                        else:
+                            verifyTotalIntents = temp
                         break
-                    intentVerify = main.FALSE
+                        main.log.info("Total Intents: {}".format( totalIntents) )
                     k = k+1
-                if not intentVerify:
+
+                totalFlows = main.CLIs[0].getTotalFlowsNum( timeout=600, noExit=True )
+                if flowTemp<totalFlows:
+                    flowTemp = totalFlows
+                else:
+                    totalFlows = flowTemp
+
+                if not intentsState:
                     # If some intents are not installed, grep the previous flows list, and finished this test case
                     main.log.warn( "Some intens did not install" )
-                    # We don't want to check flows if intents not installed, because onos will drop flows
-                    if currFlows == 0:
-                    # If currFlows equal 0, which means we failed to install intents at first, or we didn't get
-                    # the correct number, so we need get flows here.
-                        flowsState = json.loads( main.ONOSrest1.flows() )
+                    verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                    main.log.info("Total Intents: {}".format( totalIntents) )
                     break
 
-                main.log.info("Verify Flows states")
-                k = 1
-                flowsVerify = main.TRUE
-                while k <= main.verifyAttempts:
-                    # while loop for check flows by using REST api
-                    time.sleep(3)
-                    temp = 0
-                    flowsStateCount = []
-                    flowsState = json.loads( main.ONOSrest1.flows() )
-                    main.log.info("Total flows now: {}".format(len(flowsState)))
-                    if ( flowsNum < len(flowsState) ):
-                        flowsNum = len(flowsState)
-                    print(flowsNum)
-                    for f in flowsState:
-                        # get PENDING_ADD flows
-                        if f.get("state") == "PENDING_ADD":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get PENDING_REMOVE flows
-                        if f.get("state") == "PENDING_REMOVE":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get REMOVED flows
-                        if f.get("state") == "REMOVED":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-
-                    for f in flowsState:
-                        # get FAILED flwos
-                        if f.get("state") == "FAILED":
-                            temp = temp + 1
-
-                    flowsStateCount.append(temp)
-                    temp = 0
-                    k = k + 1
-                    for c in flowsStateCount:
-                        if int(c) > 0:
-                            flowsVerify = main.FALSE
-
-                    main.log.info( "Check flows States:" )
-                    main.log.info( "PENDING_ADD: {}".format( flowsStateCount[0]) )
-                    main.log.info( "PENDING_REMOVE: {}".format( flowsStateCount[1]) )
-                    main.log.info( "REMOVED: {}".format( flowsStateCount[2]) )
-                    main.log.info( "FAILED: {}".format( flowsStateCount[3]) )
-
-                    if flowsVerify == main.TRUE:
-                        break
-
         del main.scale[0]
         utilities.assert_equals( expect = main.TRUE,
-                                 actual = intentVerify,
+                                 actual = intentsState,
                                  onpass = "Successfully pushed and verified intents",
                                  onfail = "Failed to push and verify intents" )
 
-        # we need the total intents before crash
-        totalIntents = len(intentsState)
-        totalFlows = flowsNum
-
         main.log.info( "Total Intents Installed before crash: {}".format( totalIntents ) )
         main.log.info( "Total Flows ADDED before crash: {}".format( totalFlows ) )
 
         main.step('clean up Mininet')
         main.Mininet1.stopNet()
-
         main.log.info("Writing results to DS file")
         with open(main.dbFileName, "a") as dbFile:
             # Scale number
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/Dependency/rerouteTopo.py b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/dependencies/rerouteTopo.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/Dependency/rerouteTopo.py
rename to TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/dependencies/rerouteTopo.py
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/Dependency/startUp.py b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/dependencies/startUp.py
similarity index 100%
rename from TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/Dependency/startUp.py
rename to TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/dependencies/startUp.py
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/README b/TestON/tests/USECASE/USECASE_ReactiveRouting/README
new file mode 100644
index 0000000..de225b9
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/README
@@ -0,0 +1,6 @@
+Please ask Pingping(pingping@onlab.us) for topology figure.
+
+The default route 0.0.0.0 will have a flow entry look like blow inside the internal switch, which connects host inside SDN network.
+ cookie=0x17000048f91093, duration=3134.048s, table=0, n_packets=1978, n_bytes=193613, idle_age=0, priority=100,ip,in_port=3 actions=mod_dl_dst:00:00:00:00:00:04,output:1
+
+00:00:00:00:00:04 is the next hop peer MAC address.
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/USECASE_ReactiveRoutingI2MN.py b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/USECASE_ReactiveRoutingI2MN.py
new file mode 100755
index 0000000..5fca446
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/USECASE_ReactiveRoutingI2MN.py
@@ -0,0 +1,366 @@
+#!/usr/bin/python
+
+"""
+Set up the SDN-IP topology as same as it on Internet2
+"""
+
+"""
+AS 64513, (SDN AS)
+AS 64514, reachable by 10.0.4.1
+AS 64515, reachable by 10.0.5.1
+AS 64516, reachable by 10.0.6.1
+"""
+
+from mininet.net import Mininet
+from mininet.node import Controller, RemoteController
+from mininet.log import setLogLevel, info
+from mininet.cli import CLI
+from mininet.topo import Topo
+from mininet.util import quietRun
+from mininet.moduledeps import pathCheck
+
+import os.path
+import time
+from subprocess import Popen, STDOUT, PIPE
+
+QUAGGA_DIR = '/usr/lib/quagga'
+QUAGGA_RUN_DIR = '/usr/local/var/run/quagga'
+QUAGGA_CONFIG_DIR = '~/OnosSystemTest/TestON/tests/ReactiveRouting/Dependency/'
+onos1IP = '10.128.4.52'
+numSw = 39
+
+
+class SDNTopo( Topo ):
+    "SDN Topology"
+
+    def __init__( self, *args, **kwargs ):
+
+        Topo.__init__( self, *args, **kwargs )
+
+        # BGP peer hosts
+        peer64514 = self.addHost( 'peer64514' )
+        peer64515 = self.addHost( 'peer64515' )
+        peer64516 = self.addHost( 'peer64516' )
+
+        sw1 = self.addSwitch( 'sw1', dpid = '00000000000000a1' )
+        sw2 = self.addSwitch( 'sw2', dpid = '00000000000000a2' )
+        sw3 = self.addSwitch( 'sw3', dpid = '00000000000000a3' )
+        sw4 = self.addSwitch( 'sw4', dpid = '00000000000000a4' )
+        sw5 = self.addSwitch( 'sw5', dpid = '00000000000000a5' )
+        sw6 = self.addSwitch( 'sw6', dpid = '00000000000000a6' )
+        sw7 = self.addSwitch( 'sw7', dpid = '00000000000000a7' )
+        sw8 = self.addSwitch( 'sw8', dpid = '00000000000000a8' )
+        sw9 = self.addSwitch( 'sw9', dpid = '00000000000000a9' )
+        sw10 = self.addSwitch( 'sw10', dpid = '0000000000000a10' )
+        sw11 = self.addSwitch( 'sw11', dpid = '0000000000000a11' )
+        sw12 = self.addSwitch( 'sw12', dpid = '0000000000000a12' )
+        sw13 = self.addSwitch( 'sw13', dpid = '0000000000000a13' )
+        sw14 = self.addSwitch( 'sw14', dpid = '0000000000000a14' )
+        sw15 = self.addSwitch( 'sw15', dpid = '0000000000000a15' )
+        sw16 = self.addSwitch( 'sw16', dpid = '0000000000000a16' )
+        sw17 = self.addSwitch( 'sw17', dpid = '0000000000000a17' )
+        sw18 = self.addSwitch( 'sw18', dpid = '0000000000000a18' )
+        sw19 = self.addSwitch( 'sw19', dpid = '0000000000000a19' )
+        sw20 = self.addSwitch( 'sw20', dpid = '0000000000000a20' )
+        sw21 = self.addSwitch( 'sw21', dpid = '0000000000000a21' )
+        sw22 = self.addSwitch( 'sw22', dpid = '0000000000000a22' )
+        sw23 = self.addSwitch( 'sw23', dpid = '0000000000000a23' )
+        sw24 = self.addSwitch( 'sw24', dpid = '0000000000000a24' )
+        sw25 = self.addSwitch( 'sw25', dpid = '0000000000000a25' )
+        sw26 = self.addSwitch( 'sw26', dpid = '0000000000000a26' )
+        sw27 = self.addSwitch( 'sw27', dpid = '0000000000000a27' )
+        sw28 = self.addSwitch( 'sw28', dpid = '0000000000000a28' )
+        sw29 = self.addSwitch( 'sw29', dpid = '0000000000000a29' )
+        sw30 = self.addSwitch( 'sw30', dpid = '0000000000000a30' )
+        sw31 = self.addSwitch( 'sw31', dpid = '0000000000000a31' )
+        sw32 = self.addSwitch( 'sw32', dpid = '0000000000000a32' )
+        sw33 = self.addSwitch( 'sw33', dpid = '0000000000000a33' )
+        sw34 = self.addSwitch( 'sw34', dpid = '0000000000000a34' )
+        sw35 = self.addSwitch( 'sw35', dpid = '0000000000000a35' )
+        sw36 = self.addSwitch( 'sw36', dpid = '0000000000000a36' )
+        sw37 = self.addSwitch( 'sw37', dpid = '0000000000000a37' )
+        sw38 = self.addSwitch( 'sw38', dpid = '0000000000000a38' )
+        sw39 = self.addSwitch( 'sw39', dpid = '0000000000000a39' )
+
+
+        # Add a layer2 switch for control plane connectivity
+        # This switch isn't part of the SDN topology
+        # We'll use the ovs-controller to turn this into a learning switch
+        swCtl100 = self.addSwitch( 'swCtl100', dpid = '0000000000000100' )
+
+
+        # BGP speaker hosts
+        speaker1 = self.addHost( 'speaker1' )
+        speaker2 = self.addHost( 'speaker2' )
+
+        root = self.addHost( 'root', inNamespace = False , ip = '0' )
+
+        # hosts behind each AS
+        host64514 = self.addHost( 'host64514' )
+        host64515 = self.addHost( 'host64515' )
+        host64516 = self.addHost( 'host64516' )
+
+        host1 = self.addHost( 'host1' )
+        host2 = self.addHost( 'host2' )
+        host6 = self.addHost( 'host6' )
+        host13 = self.addHost( 'host13' )
+
+        self.addLink( 'speaker1', sw24 )
+        self.addLink( 'speaker2', sw24 )
+
+        # connect all switches
+        self.addLink( sw1, sw2 )
+        self.addLink( sw1, sw6 )
+        self.addLink( sw1, sw8 )
+        self.addLink( sw2, sw3 )
+        self.addLink( sw3, sw4 )
+        self.addLink( sw3, sw5 )
+        self.addLink( sw4, sw8 )
+        self.addLink( sw5, sw7 )
+        self.addLink( sw5, sw9 )
+        self.addLink( sw6, sw13 )
+        self.addLink( sw7, sw8 )
+        self.addLink( sw8, sw11 )
+        self.addLink( sw9, sw10 )
+        self.addLink( sw10, sw12 )
+        self.addLink( sw11, sw12 )
+        self.addLink( sw11, sw14 )
+        self.addLink( sw12, sw17 )
+        self.addLink( sw13, sw14 )
+        self.addLink( sw13, sw21 )
+        self.addLink( sw14, sw15 )
+        self.addLink( sw14, sw18 )
+        self.addLink( sw14, sw23 )
+        self.addLink( sw15, sw16 )
+        self.addLink( sw16, sw17 )
+        self.addLink( sw17, sw19 )
+        self.addLink( sw17, sw20 )
+        self.addLink( sw18, sw23 )
+        self.addLink( sw19, sw27 )
+        self.addLink( sw20, sw28 )
+        self.addLink( sw21, sw22 )
+        self.addLink( sw21, sw29 )
+        self.addLink( sw22, sw23 )
+        self.addLink( sw23, sw24 )
+        self.addLink( sw23, sw31 )
+        self.addLink( sw24, sw25 )
+        self.addLink( sw25, sw26 )
+        self.addLink( sw26, sw27 )
+        self.addLink( sw27, sw28 )
+        self.addLink( sw27, sw34 )
+        self.addLink( sw29, sw30 )
+        self.addLink( sw29, sw35 )
+        self.addLink( sw30, sw31 )
+        self.addLink( sw31, sw32 )
+        self.addLink( sw32, sw33 )
+        self.addLink( sw32, sw39 )
+        self.addLink( sw33, sw34 )
+        self.addLink( sw35, sw36 )
+        self.addLink( sw36, sw37 )
+        self.addLink( sw37, sw38 )
+        self.addLink( sw38, sw39 )
+
+        # connection between switches and peers
+        self.addLink( peer64514, sw32 )
+        self.addLink( peer64515, sw8 )
+        self.addLink( peer64516, sw28 )
+
+        # connection between BGP peer and hosts behind the BGP peer
+        self.addLink( peer64514, host64514 )
+        self.addLink( peer64515, host64515 )
+        self.addLink( peer64516, host64516 )
+
+        self.addLink( sw1, host1 )
+        self.addLink( sw2, host2 )
+        self.addLink( sw6, host6 )
+        self.addLink( sw13, host13 )
+
+        # Internal Connection To Hosts
+        self.addLink( swCtl100, peer64514 )
+        self.addLink( swCtl100, peer64515 )
+        self.addLink( swCtl100, peer64516 )
+        self.addLink( swCtl100, speaker1 )
+        self.addLink( swCtl100, speaker2 )
+
+
+
+        # add host64514 to control plane for ping test
+        self.addLink( swCtl100, host64514 )
+        self.addLink( swCtl100, root )
+
+
+def startsshd( host ):
+    "Start sshd on host"
+    info( '*** Starting sshd\n' )
+    name, intf, ip = host.name, host.defaultIntf(), host.IP()
+    banner = '/tmp/%s.banner' % name
+    host.cmd( 'echo "Welcome to %s at %s" >  %s' % ( name, ip, banner ) )
+    host.cmd( '/usr/sbin/sshd -o "Banner %s"' % banner, '-o "UseDNS no"' )
+    info( '***', host.name, 'is running sshd on', intf, 'at', ip, '\n' )
+
+def startsshds ( hosts ):
+    for h in hosts:
+        startsshd( h )
+
+def stopsshd():
+    "Stop *all* sshd processes with a custom banner"
+    info( '*** Shutting down stale sshd/Banner processes ',
+          quietRun( "pkill -9 -f Banner" ), '\n' )
+
+def startquagga( host, num, config_file ):
+    info( '*** Starting Quagga on %s\n' % host )
+    host.cmd( "cd %s" % QUAGGA_CONFIG_DIR )
+    zebra_cmd = \
+    '%s/zebra -d -f  ./zebra.conf -z %s/zserv%s.api -i %s/zebra%s.pid'\
+     % ( QUAGGA_DIR, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num )
+    quagga_cmd = '%s/bgpd -d -f %s -z %s/zserv%s.api -i %s/bgpd%s.pid' \
+    % ( QUAGGA_DIR, config_file, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num )
+
+    print zebra_cmd
+    print quagga_cmd
+
+    host.cmd( zebra_cmd )
+    host.cmd( quagga_cmd )
+
+def startquaggahost5( host, num ):
+    info( '*** Starting Quagga on %s\n' % host )
+    zebra_cmd = \
+    '%s/zebra -d -f  ./zebra.conf -z %s/zserv%s.api -i %s/zebra%s.pid' \
+    % ( QUAGGA_DIR, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num )
+    quagga_cmd = \
+    '%s/bgpd -d -f ./as4quaggas/quagga%s.conf -z %s/zserv%s.api -i %s/bgpd%s.pid'\
+     % ( QUAGGA_DIR, num, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num )
+
+    host.cmd( zebra_cmd )
+    host.cmd( quagga_cmd )
+
+
+def stopquagga():
+    quietRun( 'sudo pkill -9 -f bgpd' )
+    quietRun( 'sudo pkill -9 -f zebra' )
+
+def sdn1net():
+    topo = SDNTopo()
+    info( '*** Creating network\n' )
+   # time.sleep( 30 )
+    net = Mininet( topo = topo, controller = RemoteController )
+
+
+    speaker1, speaker2, peer64514, peer64515, peer64516 = \
+    net.get( 'speaker1', 'speaker2' ,
+             'peer64514', 'peer64515', 'peer64516' )
+
+    # Adding addresses to host64513_1 interface connected to sw24
+    # for BGP peering
+    speaker1.setMAC( '00:00:00:00:00:01', 'speaker1-eth0' )
+    speaker1.cmd( 'ip addr add 10.0.4.101/24 dev speaker1-eth0' )
+    speaker1.cmd( 'ip addr add 10.0.5.101/24 dev speaker1-eth0' )
+    speaker1.cmd( 'ip addr add 10.0.6.101/24 dev speaker1-eth0' )
+
+    speaker1.defaultIntf().setIP( '10.1.4.101/24' )
+    speaker1.defaultIntf().setMAC( '00:00:00:00:00:01' )
+
+    # Net has to be start after adding the above link
+    net.start()
+
+    # setup configuration on the interface connected to switch
+    peer64514.cmd( "ifconfig  peer64514-eth0 10.0.4.1 up" )
+    peer64514.setMAC( '00:00:00:00:00:04', 'peer64514-eth0' )
+    peer64515.cmd( "ifconfig  peer64515-eth0 10.0.5.1 up" )
+    peer64515.setMAC( '00:00:00:00:00:05', 'peer64515-eth0' )
+    peer64516.cmd( "ifconfig  peer64516-eth0 10.0.6.1 up" )
+    peer64516.setMAC( '00:00:00:00:00:06', 'peer64516-eth0' )
+
+    # setup configuration on the interface connected to hosts
+    peer64514.setIP( "4.0.0.254", 8, "peer64514-eth1" )
+    peer64514.setMAC( '00:00:00:00:00:44', 'peer64514-eth1' )
+    peer64515.setIP( "5.0.0.254", 8, "peer64515-eth1" )
+    peer64515.setMAC( '00:00:00:00:00:55', 'peer64515-eth1' )
+    peer64516.setIP( "6.0.0.254", 8, "peer64516-eth1" )
+    peer64516.setMAC( '00:00:00:00:00:66', 'peer64516-eth1' )
+
+    # enable forwarding on BGP peer hosts
+    peer64514.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' )
+    peer64515.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' )
+    peer64516.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' )
+
+    # config interface for control plane connectivity
+    peer64514.setIP( "192.168.0.4", 24, "peer64514-eth2" )
+    peer64515.setIP( "192.168.0.5", 24, "peer64515-eth2" )
+    peer64516.setIP( "192.168.0.6", 24, "peer64516-eth2" )
+
+    # Setup hosts in each non-SDN AS
+    host64514, host64515, host64516 = \
+    net.get( 'host64514', 'host64515', 'host64516' )
+    host64514.cmd( 'ifconfig host64514-eth0 4.0.0.1 up' )
+    host64514.cmd( 'ip route add default via 4.0.0.254' )
+    host64514.setIP( '192.168.0.44', 24, 'host64514-eth1' )  # for control plane
+    host64515.cmd( 'ifconfig host64515-eth0 5.0.0.1 up' )
+    host64515.cmd( 'ip route add default via 5.0.0.254' )
+    host64516.cmd( 'ifconfig host64516-eth0 6.0.0.1 up' )
+    host64516.cmd( 'ip route add default via 6.0.0.254' )
+
+    host1, host2, host6, host13 = \
+    net.get( 'host1', 'host2', 'host6', 'host13' )
+    host1.cmd( 'ifconfig host1-eth0 201.0.0.1 up' )
+    host1.cmd( 'route add default gw 201.0.0.254' )
+    host2.cmd( 'ifconfig host2-eth0 202.0.0.1 up' )
+    host2.cmd( 'route add default gw 202.0.0.254' )
+    host6.cmd( 'ifconfig host6-eth0 206.0.0.1 up' )
+    host6.cmd( 'route add default gw 206.0.0.254' )
+    host13.cmd( 'ifconfig host13-eth0 213.0.0.13 up' )
+    host13.cmd( 'route add default gw 213.0.0.254' )
+
+
+    # set up swCtl100 as a learning
+    swCtl100 = net.get( 'swCtl100' )
+    swCtl100.cmd( 'ovs-vsctl set-controller swCtl100 none' )
+    swCtl100.cmd( 'ovs-vsctl set-fail-mode swCtl100 standalone' )
+
+    # connect all switches to controller
+
+    for i in range ( 1, numSw + 1 ):
+        swX = net.get( 'sw%s' % ( i ) )
+        swX.cmd( 'ovs-vsctl set-controller sw%s tcp:%s:6653' % ( i, onos1IP ) )
+
+    # Start Quagga on border routers
+    '''
+    for i in range ( 64514, 64516 + 1 ):
+        startquagga( 'peer%s' % ( i ), i, 'quagga%s.conf' % ( i ) )
+    '''
+    startquagga( peer64514, 64514, 'quagga64514.conf' )
+    startquagga( peer64515, 64515, 'quagga64515.conf' )
+    startquagga( peer64516, 64516, 'quagga64516.conf' )
+
+    # start Quagga in SDN network
+    startquagga( speaker1, 64513, 'quagga-sdn.conf' )
+
+
+    root = net.get( 'root' )
+    root.intf( 'root-eth0' ).setIP( '1.1.1.2/24' )
+    root.cmd( 'ip addr add 192.168.0.100/24 dev root-eth0' )
+
+    speaker1.intf( 'speaker1-eth1' ).setIP( '1.1.1.1/24' )
+
+
+    stopsshd()
+
+    hosts = [ peer64514, peer64515, peer64516, host64514];
+    startsshds( hosts )
+    #
+
+    forwarding1 = '%s:2000:%s:2000' % ( '1.1.1.2', onos1IP )
+    root.cmd( 'ssh -nNT -o "PasswordAuthentication no" \
+    -o "StrictHostKeyChecking no" -l sdn -L %s %s & ' % ( forwarding1, onos1IP ) )
+
+    # time.sleep( 3000000000 )
+    CLI( net )
+
+
+    stopsshd()
+    stopquagga()
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'debug' )
+    sdn1net()
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga-sdn.conf b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga-sdn.conf
new file mode 100644
index 0000000..98f2fa2
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga-sdn.conf
@@ -0,0 +1,44 @@
+! -*- bgp -*-
+!
+! BGPd sample configuratin file
+!
+! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $
+!
+hostname bgpd
+password hello
+!enable password please-set-at-here
+!
+!bgp mulitple-instance
+!
+!
+router bgp 64513
+  bgp router-id 10.0.4.101
+  timers bgp 1 3
+  !timers bgp 3 9
+  neighbor 10.0.4.1 remote-as 64514
+  neighbor 10.0.4.1 ebgp-multihop
+  neighbor 10.0.4.1 timers connect 5
+  neighbor 10.0.5.1 remote-as 64515
+  neighbor 10.0.5.1 ebgp-multihop
+  neighbor 10.0.5.1 timers connect 5
+  neighbor 10.0.6.1 remote-as 64516
+  neighbor 10.0.6.1 ebgp-multihop
+  neighbor 10.0.6.1 timers connect 5
+
+  neighbor 1.1.1.2 remote-as 64513
+  neighbor 1.1.1.2 port 2000
+  neighbor 1.1.1.2 timers connect 5
+
+  network 201.0.0.0/24
+  network 202.0.0.0/24
+  !network 206.0.0.0/24
+!
+! access-list all permit any
+!
+!route-map set-nexthop permit 10
+! match ip address all
+! set ip next-hop 10.0.0.1
+!
+!log file /usr/local/var/log/quagga/bgpd.log
+!
+log stdout
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64514.conf b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64514.conf
new file mode 100644
index 0000000..09440af
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64514.conf
@@ -0,0 +1,28 @@
+! -*- bgp -*-
+!
+! BGPd sample configuratin file
+!
+! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $
+!
+hostname bgpd
+password hello
+!enable password please-set-at-here
+!
+!bgp mulitple-instance
+!
+router bgp 64514
+  bgp router-id 10.0.4.1
+!  timers bgp 1 3
+ neighbor 10.0.4.101 remote-as 64513
+ network 4.0.0.0/24
+
+!
+! access-list all permit any
+!
+!route-map set-nexthop permit 10
+! match ip address all
+! set ip next-hop 10.0.0.1
+!
+!log file /usr/local/var/log/quagga/bgpd.log
+!
+log stdout
\ No newline at end of file
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64515.conf b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64515.conf
new file mode 100644
index 0000000..6d0b701
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64515.conf
@@ -0,0 +1,28 @@
+! -*- bgp -*-
+!
+! BGPd sample configuratin file
+!
+! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $
+!
+hostname bgpd
+password hello
+!enable password please-set-at-here
+!
+!bgp mulitple-instance
+!
+router bgp 64515
+  bgp router-id 10.0.5.1
+!  timers bgp 1 3
+ neighbor 10.0.5.101 remote-as 64513
+ network 5.0.0.0/24
+
+!
+! access-list all permit any
+!
+!route-map set-nexthop permit 10
+! match ip address all
+! set ip next-hop 10.0.0.1
+!
+!log file /usr/local/var/log/quagga/bgpd.log
+!
+log stdout
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64516.conf b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64516.conf
new file mode 100644
index 0000000..5401c05
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/quagga64516.conf
@@ -0,0 +1,31 @@
+! -*- bgp -*-
+!
+! BGPd sample configuratin file
+!
+! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $
+!
+hostname bgpd
+password hello
+!enable password please-set-at-here
+!
+!bgp mulitple-instance
+!
+router bgp 64516
+  bgp router-id 10.0.6.1
+!  timers bgp 1 3
+ neighbor 10.0.6.101 remote-as 64513
+ network 6.0.0.0/24
+
+! neighbor 10.0.0.2 route-map set-nexthop out
+! neighbor 10.0.0.2 ebgp-multihop
+! neighbor 10.0.0.2 next-hop-self
+!
+! access-list all permit any
+!
+!route-map set-nexthop permit 10
+! match ip address all
+! set ip next-hop 10.0.0.1
+!
+!log file /usr/local/var/log/quagga/bgpd.log
+!
+log stdout
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/zebra.conf b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/zebra.conf
new file mode 100644
index 0000000..517db94
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/dependencies/zebra.conf
@@ -0,0 +1,26 @@
+! -*- zebra -*-
+!
+! zebra sample configuration file
+!
+! $Id: zebra.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
+!
+hostname zebra
+password hello
+enable password 0fw0rk
+log stdout
+!
+! Interfaces description.
+!
+!interface lo
+! description test of desc.
+!
+!interface sit0
+! multicast
+
+!
+! Static default route sample.
+!
+!ip route 0.0.0.0/0 203.181.89.241
+!
+
+!log file /usr/local/var/log/quagga/zebra.log
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json
new file mode 100644
index 0000000..cd1065f
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json
@@ -0,0 +1,76 @@
+{
+    "ports" : {
+        "of:00000000000000a8/5" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.5.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a32/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.4.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a28/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.6.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a1/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "201.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a2/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "202.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a6/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "206.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a13/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "213.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        }
+    },
+    "apps" : {
+        "org.onosproject.router" : {
+            "bgp" : {
+                "bgpSpeakers" : [
+                    {
+                        "connectPoint" : "of:0000000000000a24/1",
+                        "peers" : [
+                            "10.0.4.1",
+                            "10.0.5.1",
+                            "10.0.6.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+}
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withBGP b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withBGP
new file mode 100644
index 0000000..cd1065f
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withBGP
@@ -0,0 +1,76 @@
+{
+    "ports" : {
+        "of:00000000000000a8/5" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.5.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a32/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.4.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a28/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "10.0.6.101/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a1/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "201.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a2/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "202.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a6/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "206.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a13/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "213.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        }
+    },
+    "apps" : {
+        "org.onosproject.router" : {
+            "bgp" : {
+                "bgpSpeakers" : [
+                    {
+                        "connectPoint" : "of:0000000000000a24/1",
+                        "peers" : [
+                            "10.0.4.1",
+                            "10.0.5.1",
+                            "10.0.6.1"
+                        ]
+                    }
+                ]
+            }
+        }
+    }
+}
diff --git a/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withoutBGP b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withoutBGP
new file mode 100644
index 0000000..8d21d37
--- /dev/null
+++ b/TestON/tests/USECASE/USECASE_ReactiveRouting/network-cfg.json.withoutBGP
@@ -0,0 +1,36 @@
+{
+    "ports" : {
+        "of:00000000000000a1/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "201.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a2/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "202.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:00000000000000a6/3" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "206.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        },
+        "of:0000000000000a13/4" : {
+            "interfaces" : [
+                {
+                    "ips"  : [ "213.0.0.200/24" ],
+                    "mac"  : "00:00:00:00:00:01"
+                }
+            ]
+        }
+    }
+}
diff --git a/TestON/tests/USECASE/USECASE_SdnipFunctionCluster_fsfw/USECASE_SdnipFunctionCluster_fsfw.topo b/TestON/tests/USECASE/USECASE_SdnipFunctionCluster_fsfw/USECASE_SdnipFunctionCluster_fsfw.topo
index c6d57fc..2dbca1d 100644
--- a/TestON/tests/USECASE/USECASE_SdnipFunctionCluster_fsfw/USECASE_SdnipFunctionCluster_fsfw.topo
+++ b/TestON/tests/USECASE/USECASE_SdnipFunctionCluster_fsfw/USECASE_SdnipFunctionCluster_fsfw.topo
@@ -27,12 +27,21 @@
             <COMPONENTS> </COMPONENTS>
         </ONOScli2>
 
+        <ONOScli3>
+            <host>127.0.0.1</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOScli3>
+
         <QuaggaCliSpeaker1>
             <host>127.0.0.1</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>QuaggaCliDriver</type>
-            <connect_order>4</connect_order>
+            <connect_order>5</connect_order>
             <COMPONENTS> </COMPONENTS>
         </QuaggaCliSpeaker1>
 
@@ -41,7 +50,7 @@
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>5</connect_order>
+            <connect_order>6</connect_order>
             <COMPONENTS>
                 <home>~/Mininet/mininet/custom/</home>
             </COMPONENTS>
diff --git a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.py b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.py
index 9183974..d995156 100644
--- a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.py
+++ b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.py
@@ -154,7 +154,7 @@
                                  onfail="ONOS is NOT up" )
 
         main.step( "Checking if ONOS CLI is ready" )
-        cliResult = main.ONOScli.startOnosCli( ONOS1Ip,
+        cliResult = main.ONOScli1.startOnosCli( ONOS1Ip,
                                                commandlineTimeout=100,
                                                onosStartTimeout=600 )
         utilities.assert_equals( expect=main.TRUE,
@@ -164,7 +164,7 @@
 
         for i in range( 10 ):
             ready = True
-            output = main.ONOScli.summary()
+            output = main.ONOScli1.summary()
             if not output:
                 ready = False
             if ready:
@@ -181,20 +181,20 @@
 
         main.log.info( "Get links in the network" )
         time.sleep( int ( main.params['timers']['TopoDiscovery'] ) )
-        summaryResult = main.ONOScli.summary()
+        summaryResult = main.ONOScli1.summary()
         linkNum = json.loads( summaryResult )[ "links" ]
-        listResult = main.ONOScli.links( jsonFormat=False )
+        listResult = main.ONOScli1.links( jsonFormat=False )
         main.log.info( listResult )
         if linkNum < 100:
             main.log.error( "Link number is wrong!" )
             time.sleep( int( main.params['timers']['TopoDiscovery'] ) )
-            listResult = main.ONOScli.links( jsonFormat=False )
+            listResult = main.ONOScli1.links( jsonFormat=False )
             main.log.info( listResult )
             main.cleanup()
             main.exit()
 
         main.step( "Activate sdn-ip application" )
-        activeSDNIPresult = main.ONOScli.activateApp( "org.onosproject.sdnip" )
+        activeSDNIPresult = main.ONOScli1.activateApp( "org.onosproject.sdnip" )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=activeSDNIPresult,
                                  onpass="Activate SDN-IP succeeded",
@@ -248,13 +248,13 @@
                        % main.params[ 'config' ][ 'peerNum' ] )
         main.step( "Check P2P intents number from ONOS CLI" )
 
-        getIntentsResult = main.ONOScli.intents( jsonFormat=True )
+        getIntentsResult = main.ONOScli1.intents( jsonFormat=True )
         bgpIntentsActualNum = \
             main.QuaggaCliSpeaker1.extractActualBgpIntentNum( getIntentsResult )
         bgpIntentsExpectedNum = int( main.params[ 'config' ][ 'peerNum' ] ) * 6
         if bgpIntentsActualNum != bgpIntentsExpectedNum:
             time.sleep( int( main.params['timers']['RouteDelivery'] ) )
-            getIntentsResult = main.ONOScli.intents( jsonFormat=True )
+            getIntentsResult = main.ONOScli1.intents( jsonFormat=True )
             bgpIntentsActualNum = \
                 main.QuaggaCliSpeaker1.extractActualBgpIntentNum( getIntentsResult )
         main.log.info( "bgpIntentsExpected num is:" )
@@ -280,14 +280,14 @@
         allRoutesExpected.append( "5.0.0.0/24" + "/" + "10.0.5.1" )
         allRoutesExpected.append( "6.0.0.0/24" + "/" + "10.0.6.1" )
 
-        getRoutesResult = main.ONOScli.routes( jsonFormat=True )
+        getRoutesResult = main.ONOScli1.routes( jsonFormat=True )
         allRoutesActual = \
             main.QuaggaCliSpeaker1.extractActualRoutesMaster( getRoutesResult )
         allRoutesStrExpected = str( sorted( allRoutesExpected ) )
         allRoutesStrActual = str( allRoutesActual ).replace( 'u', "" )
         if allRoutesStrActual != allRoutesStrExpected:
             time.sleep( int( main.params['timers']['RouteDelivery'] ) )
-            getRoutesResult = main.ONOScli.routes( jsonFormat=True )
+            getRoutesResult = main.ONOScli1.routes( jsonFormat=True )
             allRoutesActual = \
                 main.QuaggaCliSpeaker1.extractActualRoutesMaster( getRoutesResult )
             allRoutesStrActual = str( allRoutesActual ).replace( 'u', "" )
@@ -303,13 +303,13 @@
             onfail="Routes are wrong!" )
 
         main.step( "Check M2S intents installed" )
-        getIntentsResult = main.ONOScli.intents( jsonFormat=True )
+        getIntentsResult = main.ONOScli1.intents( jsonFormat=True )
         routeIntentsActualNum = \
             main.QuaggaCliSpeaker1.extractActualRouteIntentNum( getIntentsResult )
         routeIntentsExpectedNum = 3
         if routeIntentsActualNum != routeIntentsExpectedNum:
             time.sleep( int( main.params['timers']['RouteDelivery'] ) )
-            getIntentsResult = main.ONOScli.intents( jsonFormat=True )
+            getIntentsResult = main.ONOScli1.intents( jsonFormat=True )
             routeIntentsActualNum = \
                 main.QuaggaCliSpeaker1.extractActualRouteIntentNum( getIntentsResult )
 
@@ -620,9 +620,9 @@
         check route number, P2P intent number, M2S intent number, ping test" )
 
         main.log.info( "Check the flow number correctness before stopping sw11" )
-        main.Functions.checkFlowNum( main, "sw11", 13 )
-        main.Functions.checkFlowNum( main, "sw1", 3 )
-        main.Functions.checkFlowNum( main, "sw7", 3 )
+        main.Functions.checkFlowNum( main, "sw11", 19 )
+        main.Functions.checkFlowNum( main, "sw1", 9 )
+        main.Functions.checkFlowNum( main, "sw7", 6 )
         main.log.debug( main.Mininet.checkFlows( "sw11" ) )
         main.log.debug( main.Mininet.checkFlows( "sw1" ) )
         main.log.debug( main.Mininet.checkFlows( "sw7" ) )
@@ -671,8 +671,8 @@
         check route number, P2P intent number, M2S intent number, ping test" )
 
         main.log.info( "Check the flow status before starting sw11" )
-        main.Functions.checkFlowNum( main, "sw1", 11 )
-        main.Functions.checkFlowNum( main, "sw7", 5 )
+        main.Functions.checkFlowNum( main, "sw1", 17 )
+        main.Functions.checkFlowNum( main, "sw7", 8 )
         main.log.debug( main.Mininet.checkFlows( "sw1" ) )
         main.log.debug( main.Mininet.checkFlows( "sw7" ) )
 
diff --git a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.topo b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.topo
index 01be5af..9ba2e9e 100644
--- a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.topo
+++ b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/USECASE_SdnipFunction_fsfw.topo
@@ -10,14 +10,14 @@
             <COMPONENTS> </COMPONENTS>
         </ONOSbench>
 
-        <ONOScli>
+        <ONOScli1>
             <host>127.0.0.1</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>OnosCliDriver</type>
             <connect_order>2</connect_order>
             <COMPONENTS> </COMPONENTS>
-        </ONOScli>
+        </ONOScli1>
 
         <ONOS1>
             <host>OC1</host>
diff --git a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/Functions.py b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/Functions.py
index dd0b29f..ad67952 100644
--- a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/Functions.py
+++ b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/Functions.py
@@ -7,10 +7,10 @@
     main.log.info( routeNumExpected )
     main.log.info( "Route number from ONOS CLI:" )
 
-    routeNumActual = main.ONOScli.ipv4RouteNumber()
+    routeNumActual = main.ONOScli1.ipv4RouteNumber()
     if routeNumActual != routeNumExpected:
         time.sleep( wait )
-        routeNumActual = main.ONOScli.ipv4RouteNumber()
+        routeNumActual = main.ONOScli1.ipv4RouteNumber()
     main.log.info( routeNumActual )
     utilities.assertEquals( \
         expect = routeNumExpected, actual = routeNumActual,
@@ -24,12 +24,12 @@
     main.log.info( "Intent number expected:" )
     main.log.info( intentNumExpected )
     main.log.info( "Intent number from ONOS CLI:" )
-    jsonResult = main.ONOScli.intents( jsonFormat = True, summary = True,
+    jsonResult = main.ONOScli1.intents( jsonFormat = True, summary = True,
                                        TYPE = "multiPointToSinglePoint" )
     intentNumActual = jsonResult['installed']
     if intentNumActual != intentNumExpected:
         time.sleep( wait )
-        jsonResult = main.ONOScli.intents( jsonFormat = True, summary = True,
+        jsonResult = main.ONOScli1.intents( jsonFormat = True, summary = True,
                                            TYPE = "multiPointToSinglePoint" )
         intentNumActual = jsonResult['installed']
     main.log.info( intentNumActual )
@@ -45,13 +45,13 @@
     main.log.info( "Intent number expected:" )
     main.log.info( intentNumExpected )
     main.log.info( "Intent number from ONOS CLI:" )
-    jsonResult = main.ONOScli.intents( jsonFormat = True, summary = True,
+    jsonResult = main.ONOScli1.intents( jsonFormat = True, summary = True,
                                        TYPE = "pointToPoint" )
     intentNumActual = jsonResult['installed']
 
     if intentNumActual != intentNumExpected:
         time.sleep( wait )
-        jsonResult = main.ONOScli.intents( jsonFormat = True, summary = True,
+        jsonResult = main.ONOScli1.intents( jsonFormat = True, summary = True,
                                            TYPE = "pointToPoint" )
         intentNumActual = jsonResult['installed']
     main.log.info( intentNumActual )
diff --git a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/fsfw.xml b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/fsfw.xml
index e792f37..8fee131 100644
--- a/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/fsfw.xml
+++ b/TestON/tests/USECASE/USECASE_SdnipFunction_fsfw/dependencies/fsfw.xml
@@ -434,7 +434,7 @@
             </port>
         </switch>
 
-        <controller ip_address="10.128.20.11" ssl="false" port="6633"/>
+        <controller ip_address="10.254.1.201" ssl="false" port="6633"/>
     </slice>
 
 </flowspace_firewall>
\ No newline at end of file
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/2x2.json b/TestON/tests/USECASE/USECASE_SegmentRouting/2x2.json
index 31b2cc8..a5ec00d 100644
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/2x2.json
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/2x2.json
@@ -36,7 +36,7 @@
                 "name" : "Leaf-R1",
                 "nodeSid" : 1,
                 "routerIp" : "192.168.0.1",
-                "routerMac" : "00:00:00:00:00:01",
+                "routerMac" : "10:00:00:00:00:01",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -47,7 +47,7 @@
                 "name" : "Leaf-R2",
                 "nodeSid" : 2,
                 "routerIp" : "192.168.0.2",
-                "routerMac" : "00:00:00:00:00:02",
+                "routerMac" : "10:00:00:00:00:02",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -58,7 +58,7 @@
                 "name" : "Spine-R1",
                 "nodeSid" : 101,
                 "routerIp" : "192.168.0.101",
-                "routerMac" : "00:00:00:00:01:01",
+                "routerMac" : "10:00:00:00:01:01",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
@@ -69,7 +69,7 @@
                 "name" : "Spine-R2",
                 "nodeSid" : 102,
                 "routerIp" : "192.168.0.102",
-                "routerMac" : "00:00:00:00:01:02",
+                "routerMac" : "10:00:00:00:01:02",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/4x4.json b/TestON/tests/USECASE/USECASE_SegmentRouting/4x4.json
index dee6bc3..7442359 100644
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/4x4.json
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/4x4.json
@@ -64,7 +64,7 @@
                 "name" : "Leaf-R1",
                 "nodeSid" : 1,
                 "routerIp" : "192.168.0.1",
-                "routerMac" : "00:00:00:00:00:01",
+                "routerMac" : "10:00:00:00:00:01",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -75,7 +75,7 @@
                 "name" : "Leaf-R2",
                 "nodeSid" : 2,
                 "routerIp" : "192.168.0.2",
-                "routerMac" : "00:00:00:00:00:02",
+                "routerMac" : "10:00:00:00:00:02",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -86,7 +86,7 @@
                 "name" : "Leaf-R3",
                 "nodeSid" : 3,
                 "routerIp" : "192.168.0.3",
-                "routerMac" : "00:00:00:00:00:03",
+                "routerMac" : "10:00:00:00:00:03",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -97,7 +97,7 @@
                 "name" : "Leaf-R4",
                 "nodeSid" : 4,
                 "routerIp" : "192.168.0.4",
-                "routerMac" : "00:00:00:00:00:04",
+                "routerMac" : "10:00:00:00:00:04",
                 "isEdgeRouter" : true,
                 "adjacencySids" : []
             }
@@ -108,7 +108,7 @@
                 "name" : "Spine-R1",
                 "nodeSid" : 101,
                 "routerIp" : "192.168.0.101",
-                "routerMac" : "00:00:00:00:01:01",
+                "routerMac" : "10:00:00:00:01:01",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
@@ -119,7 +119,7 @@
                 "name" : "Spine-R2",
                 "nodeSid" : 102,
                 "routerIp" : "192.168.0.102",
-                "routerMac" : "00:00:00:00:01:02",
+                "routerMac" : "10:00:00:00:01:02",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
@@ -130,7 +130,7 @@
                 "name" : "Spine-R3",
                 "nodeSid" : 103,
                 "routerIp" : "192.168.0.103",
-                "routerMac" : "00:00:00:00:01:03",
+                "routerMac" : "10:00:00:00:01:03",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
@@ -141,7 +141,7 @@
                 "name" : "Spine-R4",
                 "nodeSid" : 104,
                 "routerIp" : "192.168.0.104",
-                "routerMac" : "00:00:00:00:01:04",
+                "routerMac" : "10:00:00:00:01:04",
                 "isEdgeRouter" : false,
                 "adjacencySids" : []
             }
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/README.md b/TestON/tests/USECASE/USECASE_SegmentRouting/README.md
index da75079..8240068 100644
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/README.md
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/README.md
@@ -1,18 +1,33 @@
-This test is designed to verify basic connectivity the SegmentRouting application.
+This test is designed to verify basic connectivity the SegmentRouting application via pingaall.
 
 It consists of 
 
-1) Installing and Starting ONOS
-2) Starting Mininet and testing connectivity
+1) Configure and Install ONOS
+2) Start Mininet and check flow state
+2) Test connectivity
 
 Requirements
 
- - A single ONOS instance is required for the test, the application is currently not stable in a cluster.
+ - A single ONOS instance is required for the test, the application is currently not stable in a cluster as of today.
  - An updated version of the CPQD switch has to be running to make sure it supports group chaining.
 
 Step 1:
-In this step we copy the proper config file to ONOS, next we package ONOS and install it in the target machine.
+In this step, we copy config file to ONOS, next we package ONOS and install it in the target machine.
 
 Step 2:
 
-In this step we start a 2x2 leaf-spine topology, connect to ONOS, and next we send several pings between hosts to test connectivity.
+In this step we start the topology, connect to ONOS
+
+Step:3
+
+Here, we send several pings between hosts to test connectivity.
+
+Then Steps are repeated for different configurations and topologies.
+
+    Configurations:
+     1) APPS=openflow-base,netcfghostprovider,netcfglinksprovider
+     2) APPS=openflow
+
+    Topologies:
+     1) 2x2 Leaf-Spine
+     2) 4x4 Leaf-Spine
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.params b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.params
index eb55d5f..15ce439 100755
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.params
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.params
@@ -1,6 +1,6 @@
 <PARAMS>
 
-    <testcases>1,2,11,9,2,11,9,2,11,9,2,11,9</testcases>
+    <testcases>1,[2,3,4,4,10]*2</testcases>
 
     <SCALE>
         <size>1</size>
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
old mode 100644
new mode 100755
index 7eb4dd7..1a4834b
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
@@ -36,10 +36,10 @@
         main.path = os.path.dirname( main.testFile )
         main.dependencyPath = main.path + "/dependencies/"
         main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
-        #main.json = ["4x4"]
+        #main.json = ["0x1","0x1"]
         main.json = ["2x2", "2x2","4x4","4x4"]
         main.args = [" ", " ", " --spine 4 --leaf 4 ", " --spine 4 --leaf 4 "]
-        #main.args = [" --spine 4 --leaf 4 "]
+        #main.args = [" --spine 0 --leaf 1 "," --spine 0 --leaf 1 "]
         main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
         main.maxNodes = int( main.params[ 'SCALE' ][ 'max' ] )
         main.ONOSport = main.params[ 'CTRL' ][ 'port' ]
@@ -114,20 +114,14 @@
         else: main.log.error( "App list is empty" )
         main.case( "Package and start ONOS using apps:" + apps)
 
-        #kill off all onos processes
-        main.log.info( "Safety check, killing all ONOS processes" +
-                       " before initiating environment setup" )
-
-        for i in range( main.maxNodes ):
-            main.ONOSbench.onosDie( main.ONOSip[ i ] )
-
         print "NODE COUNT = ", main.numCtrls
 
         tempOnosIp = []
         for i in range( main.numCtrls ):
             tempOnosIp.append( main.ONOSip[i] )
+
         onosUser = main.params[ 'ENV' ][ 'cellUser' ]
-        main.step("Creating cell file")
+        main.step("Create and Apply cell file")
         main.ONOSbench.createCellFile( main.ONOSbench.ip_address,
                                        "temp",
                                        main.Mininet1.ip_address,
@@ -135,7 +129,6 @@
                                        tempOnosIp,
                                        onosUser )
 
-        main.step( "Apply cell to environment" )
         cellResult = main.ONOSbench.setCell( "temp" )
         verifyResult = main.ONOSbench.verifyCell()
         stepResult = cellResult and verifyResult
@@ -144,8 +137,14 @@
                                  onpass="Successfully applied cell to " + \
                                         "environment",
                                  onfail="Failed to apply cell to environment " )
+        #kill off all onos processes
+        main.log.info( "Safety check, killing all ONOS processes" +
+                       " before initiating environment setup" )
 
-        main.step( "Creating ONOS package" )
+        for i in range( main.maxNodes ):
+            main.ONOSbench.onosDie( main.ONOSip[ i ] )
+
+        main.step( "Create and Install ONOS package" )
         main.jsonFile=main.json.pop(0)
         main.ONOSbench.handle.sendline( "cp "+main.path+"/"+main.jsonFile+".json ~/onos/tools/package/config/network-cfg.json")
         packageResult = main.ONOSbench.onosPackage()
@@ -155,9 +154,8 @@
         #                         onpass="Successfully created ONOS package",
         #                         onfail="Failed to create ONOS package" )
 
-        time.sleep( main.startUpSleep )
+        #time.sleep( main.startUpSleep )
 
-        main.step( "Installing ONOS package" )
         onosInstallResult = main.TRUE
         for i in range( main.numCtrls ):
             onosInstallResult = onosInstallResult and \
@@ -187,34 +185,40 @@
                 startResult = startResult and \
                         main.ONOSbench.onosStart( main.ONOSip[ i ] )
         stepResult = onosIsUp and stopResult and startResult
+
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="ONOS service is ready",
                                  onfail="ONOS service did not start properly" )
-        time.sleep( 2*main.startUpSleep )
+        #time.sleep( 2*main.startUpSleep )
+        #main.ONOSbench.handle.sendline( "onos-secure-ssh")
+        main.step( "Checking if ONOS CLI is ready" )
+        cellResult = main.CLIs[0].setCell( "temp" )
 
-    def CASE9( self, main ):
-        '''
-            Report errors/warnings/exceptions
-        '''
-        main.case( "Logging test for " + main.jsonFile )
-        #if len(main.json) > 0 :
-        main.ONOSbench.cpLogsToDir("/opt/onos/log/karaf.log",main.logdir, 
-                           copyFileName="karaf.log."+main.jsonFile+str(len(main.json)))
-        #main.ONOSbench.logReport( main.ONOSip[ 0 ],
-        #                          [ "INFO" ],
-        #                          "a" )
-        #main.log.info("Error report: \n" )
-        main.ONOSbench.logReport( main.ONOSip[ 0 ],
-                                  [ "INFO",
-                                    "FOLLOWER",
-                                    "WARN",
-                                    "flow",
-                                    "ERROR",
-                                    "Except" ],
-                                  "s" )
+        cliResult = main.CLIs[0].startOnosCli( main.ONOSip[ 0 ],
+                                           commandlineTimeout=60, onosStartTimeout=100 )
+        utilities.assert_equals( expect=main.TRUE,
+                             actual=cliResult,
+                             onpass="ONOS CLI is ready",
+                             onfail="ONOS CLI is not ready" )
+        for i in range( 10 ):
+            ready = True
+            output = main.CLIs[0].summary()
+            if not output:
+                ready = False
+            if ready:
+                break
+            time.sleep( 10 )
+        utilities.assert_equals( expect=True, actual=ready,
+                                 onpass="ONOS summary command succeded",
+                                 onfail="ONOS summary command failed" )
 
-    def CASE11( self, main ):
+        if not ready:
+            main.log.error( "ONOS startup failed!" )
+            main.cleanup()
+            main.exit()
+
+    def CASE3( self, main ):
         """
             Start mininet
         """
@@ -240,17 +244,77 @@
         if not topoResult:
             main.cleanup()
             main.exit()
-        main.step("Waiting for switch initialization and configuration")
-        time.sleep( 3*main.startUpSleep)
+        #main.step("Waiting for switch initialization and configuration")
+        main.step(" Check whether the flow count is bigger than 116" )
+        count =  utilities.retry( main.CLIs[0].checkFlowCount,
+                                 main.FALSE,
+                                 kwargs={'min':116},
+                                 attempts=10 )
+        utilities.assertEquals( \
+            expect=True,
+            actual=(count>0),
+            onpass="Flow count looks correct: "+str(count),
+            onfail="Flow count looks wrong: "+str(count) )
+
+        main.step( "Check whether all flow status are ADDED" )
+        flowCheck = utilities.retry( main.CLIs[0].checkFlowsState,
+                                 main.FALSE,
+                                 kwargs={'isPENDING':False},
+                                 attempts=10 )
+        utilities.assertEquals( \
+            expect=main.TRUE,
+            actual=flowCheck,
+            onpass="Flow status is correct!",
+            onfail="Flow status is wrong!" )
+        main.ONOSbench.dumpFlows( main.ONOSip[0],
+                 main.logdir, "flowsBefore" + main.jsonFile)
+        main.ONOSbench.dumpGroups( main.ONOSip[0],
+                                   main.logdir, "groupsBefore" + main.jsonFile)
+        #time.sleep( 3*main.startUpSleep)
+        main.count=1
+
+    def CASE4( self, main ):
+        main.case( "Check full connectivity" )
+        main.log.report( "Check full connectivity" )
+
+        main.step("Check full connectivity"+str(main.count))
         pa = main.Mininet1.pingall()
         utilities.assert_equals( expect=main.TRUE, actual=pa,
                                  onpass="Full connectivity successfully tested",
                                  onfail="Full connectivity failed" )
         # cleanup mininet
+        main.ONOSbench.dumpFlows( main.ONOSip[0],
+                 main.logdir, "flowsAfter" + str(main.count) + main.jsonFile)
+        main.ONOSbench.dumpGroups( main.ONOSip[0],
+                           main.logdir, "groupsAfter" + str(main.count) + main.jsonFile)
+
+    def CASE10( self, main ):
+        '''
+            Report errors/warnings/exceptions
+        '''
+        main.case( "Logging test for " + main.jsonFile )
+        #if len(main.json) > 0 :
         main.ONOSbench.onosStop( main.ONOSip[0] )
         main.Mininet1.stopNet()
-
-
+        
+        main.ONOSbench.scp( main.ONOScli1 ,
+                                          "/opt/onos/log/karaf.log",
+                                          "/tmp/karaf.log",
+                                          direction="from" )
+        main.ONOSbench.cpLogsToDir("/tmp/karaf.log",main.logdir,
+                                   copyFileName="karaf.log."+main.jsonFile+str(len(main.json)))
+        #main.ONOSbench.logReport( main.ONOSip[ 0 ],
+        #                          [ "INFO" ],
+        #                          "a" )
+        #main.log.info("Error report: \n" )
+        main.ONOSbench.logReport( main.ONOSip[ 0 ],
+                                  [ "INFO",
+                                    "FOLLOWER",
+                                    "WARN",
+                                    "flow",
+                                    "ERROR",
+                                    "Except" ],
+                                  "s" )