Merge "Refactor tests to use ONOSSetup.checkOnosNodes function"
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 5318ffb..661ada7 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -1935,7 +1935,7 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
-    def getSwitchRandom( self, timeout=60, nonCut=True, switchClasses=None, excludeNodes=[], excludeSwitches=[] ):
+    def getSwitchRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipSwitches=[] ):
         """
         Randomly get a switch from Mininet topology.
         If nonCut is True, it gets a list of non-cut switches (the deletion
@@ -1944,26 +1944,25 @@
         it just randomly returns one switch from all current switches in
         Mininet.
         excludeNodes will be pased to getGraphDict method
-        Switches specified in excludeSwitches will be excluded
+        Switches specified in skipSwitches will be excluded
         Returns the name of the chosen switch.
         """
         import random
         candidateSwitches = []
         try:
             if not nonCut:
-                switches = self.getSwitches( timeout=timeout, switchClasses=switchClasses )
+                switches = self.getSwitches( timeout=timeout, excludeNodes=excludeNodes )
                 assert len( switches ) != 0
                 for switchName in switches.keys():
                     candidateSwitches.append( switchName )
             else:
                 graphDict = self.getGraphDict( timeout=timeout, useId=False,
-                                               switchClasses=switchClasses,
                                                excludeNodes=excludeNodes )
                 if graphDict is None:
                     return None
                 self.graph.update( graphDict )
                 candidateSwitches = self.graph.getNonCutVertices()
-                candidateSwitches = [ switch for switch in candidateSwitches if switch not in excludeSwitches ]
+            candidateSwitches = [ switch for switch in candidateSwitches if switch not in skipSwitches ]
             if candidateSwitches is None:
                 return None
             elif len( candidateSwitches ) == 0:
@@ -2076,7 +2075,7 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
-    def getLinkRandom( self, timeout=60, nonCut=True, switchClasses=None, excludeNodes=[] ):
+    def getLinkRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipLinks=[] ):
         """
         Randomly get a link from Mininet topology.
         If nonCut is True, it gets a list of non-cut links (the deletion
@@ -2085,6 +2084,7 @@
         it just randomly returns one link from all current links in
         Mininet.
         excludeNodes will be passed to getLinks method to exclude unexpected links.
+        Any link that has either end included in skipLinks will be excluded
         Returns the link as a list, e.g. [ 's1', 's2' ]
         """
         import random
@@ -2100,12 +2100,13 @@
                     candidateLinks.append( [ link[ 'node1' ], link[ 'node2' ] ] )
             else:
                 graphDict = self.getGraphDict( timeout=timeout, useId=False,
-                                               switchClasses=switchClasses,
                                                excludeNodes=excludeNodes )
                 if graphDict is None:
                     return None
                 self.graph.update( graphDict )
                 candidateLinks = self.graph.getNonCutEdges()
+            candidateLinks = [ link for link in candidateLinks
+                               if link[0] not in skipLinks and link[1] not in skipLinks ]
             if candidateLinks is None:
                 return None
             elif len( candidateLinks ) == 0:
@@ -2821,12 +2822,14 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
-    def getSwitches( self, verbose=False, updateTimeout=1000, switchClasses=None ):
+    def getSwitches( self, verbose=False, updateTimeout=1000, excludeNodes=[] ):
         """
         Read switches from Mininet.
 
         Returns a dictionary whose keys are the switch names and the value is
         a dictionary containing information about the switch.
+        If excludeNodes is specified, switches with names included in excludeNodes
+        will be ingored.
         """
         # NOTE: To support new Mininet switch classes, just append the new
         # class to the switchClasses variable
@@ -2838,8 +2841,7 @@
         # <OVSSwitchNS s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None,s1-eth3:None pid=22550>
         # <OVSBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=26830>
         # <UserSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=14737>
-        if not switchClasses:
-            switchClasses = r"(OVSSwitch)|(OVSBridge)|(OVSSwitchNS)|(IVSSwitch)|(LinuxBridge)|(UserSwitch)"
+        switchClasses = r"(OVSSwitch)|(OVSBridge)|(OVSSwitchNS)|(IVSSwitch)|(LinuxBridge)|(UserSwitch)"
         try:
             swRE = r"<(?P<class>" + switchClasses + r")" +\
                    r"(?P<options>\{.*\})?\s" +\
@@ -2854,6 +2856,8 @@
                 result = re.search( swRE, line, re.I )
                 if result:
                     name = result.group( 'name' )
+                    if name in excludeNodes:
+                        continue
                     dpid = str( self.getSwitchDPID( name ) ).zfill( 16 )
                     pid = result.group( 'pid' )
                     swClass = result.group( 'class' )
@@ -2967,8 +2971,8 @@
               'port1': str( port1 of_port )
               'port2': str( port2 of_port ) }
 
-        If either node1 or node2 name starts with any of the strings sepcified
-        in excludeNodes, the link will be excluded from the returned value
+        If either node1 or node2 name matches any of the names sepcified in
+        excludeNodes, the link will be excluded from the returned value
 
         Note: The port number returned is the eth#, not necessarily the of_port
               number. In Mininet, for OVS switch, these should be the same. For
@@ -2989,8 +2993,7 @@
                 if match:
                     node1 = match.group( 'node1' )
                     node2 = match.group( 'node2' )
-                    if any( node1.startswith( node ) for node in excludeNodes ) or \
-                       any( node2.startswith( node ) for node in excludeNodes ):
+                    if any( node1 == node or node2 == node for node in excludeNodes ):
                         continue
                     port1 = match.group( 'port1' )
                     port2 = match.group( 'port2' )
@@ -3461,7 +3464,7 @@
             main.cleanAndExit()
 
     def getGraphDict( self, timeout=60, useId=True, includeHost=False,
-                      switchClasses=None, excludeNodes=[] ):
+                      excludeNodes=[] ):
         """
         Return a dictionary which describes the latest Mininet topology data as a
         graph.
@@ -3480,9 +3483,8 @@
         topology.
         If includeHost == True, all hosts (and host-switch links) will be included
         in topology data.
-        if switchClasses == None, default switchClasses will be used when calling
-        getSwitches method.
-        excludeNodes will be passed to getLinks method to exclude unexpected links.
+        excludeNodes will be passed to getSwitches and getLinks methods to exclude
+        unexpected switches and links.
         Note that link or switch that are brought down by 'link x x down' or 'switch
         x down' commands still show in the output of Mininet CLI commands such as
         'links', 'dump', etc. Thus, to ensure the correctness of this function, it is
@@ -3493,11 +3495,11 @@
         try:
             links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
             portDict = {}
-            switches = self.getSwitches( switchClasses=switchClasses )
+            switches = self.getSwitches( excludeNodes=excludeNodes )
             if includeHost:
                 hosts = self.getHosts()
             for link in links:
-                # FIXME: support 'includeHost' argument
+                # TODO: support 'includeHost' argument
                 if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
                     continue
                 nodeName1 = link[ 'node1' ]
@@ -3549,7 +3551,7 @@
                     for port in switches[ nodeName1 ][ 'ports' ]:
                         if port[ 'of_port' ] == str( portIndex ):
                             # Use -1 as index for disabled port
-                            if port[ 'enabled' ] == True:
+                            if port[ 'enabled' ]:
                                 graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': portIndex }
                             else:
                                 graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': -1 }
diff --git a/TestON/drivers/common/cli/hostdriver.py b/TestON/drivers/common/cli/hostdriver.py
index d8cc74d..7c7cd80 100644
--- a/TestON/drivers/common/cli/hostdriver.py
+++ b/TestON/drivers/common/cli/hostdriver.py
@@ -38,6 +38,7 @@
         self.handle = self
         self.name = None
         self.shortName = None
+        self.interfaces = []
         self.home = None
         self.inband = False
         self.prompt = "\$"
@@ -56,6 +57,10 @@
                 vars( self )[ key ] = connectargs[ key ]
             self.name = self.options[ 'name' ]
             self.shortName = self.options[ 'shortName' ]
+            self.interfaces.append( { 'ips': [ self.options[ 'ip' ] ],
+                                      'isUp': True,
+                                      'mac': self.options[ 'mac' ],
+                                      'name': None } )
 
             try:
                 if os.getenv( str( self.ip_address ) ) is not None:
@@ -109,13 +114,26 @@
         try:
             if self.handle:
                 # Disconnect from the host
-                self.handle.sendline( "" )
-                self.handle.expect( self.prompt )
-                self.handle.sendline( "exit" )
-                i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
-                if i == 1:
-                    main.log.error( self.name + ": timeout when waiting for response" )
-                    main.log.error( "response: " + str( self.handle.before ) )
+                if not self.options[ 'inband' ] == 'True':
+                    self.handle.sendline( "" )
+                    self.handle.expect( self.prompt )
+                    self.handle.sendline( "exit" )
+                    i = self.handle.expect( [ "closed", pexpect.TIMEOUT ] )
+                    if i == 1:
+                        main.log.error( self.name + ": timeout when waiting for response" )
+                        main.log.error( "response: " + str( self.handle.before ) )
+                else:
+                    self.handle.sendline( "" )
+                    i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=2 )
+                    if i == 1:
+                        main.log.warn( self.name + ": timeout when waiting for response" )
+                        main.log.warn( "response: " + str( self.handle.before ) )
+                    self.handle.sendline( "exit" )
+                    i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
+                    if i == 1:
+                        main.log.warn( self.name + ": timeout when waiting for response" )
+                        main.log.warn( "response: " + str( self.handle.before ) )
+                return main.TRUE
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             response = main.FALSE
@@ -178,7 +196,7 @@
                 main.log.info( "Skip disconnecting the host via data plane" )
                 return main.TRUE
             self.handle.sendline( "" )
-            self.handle.expect( self.prompt )
+            self.handle.expect( self.prompt, timeout=2 )
             self.handle.sendline( "exit" )
             i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
             if i == 1:
diff --git a/TestON/drivers/common/cli/networkdriver.py b/TestON/drivers/common/cli/networkdriver.py
index 828187c..1cd242c 100755
--- a/TestON/drivers/common/cli/networkdriver.py
+++ b/TestON/drivers/common/cli/networkdriver.py
@@ -32,6 +32,7 @@
 import re
 import types
 from drivers.common.clidriver import CLI
+from core.graph import Graph
 
 class NetworkDriver( CLI ):
 
@@ -45,7 +46,9 @@
         self.handle = None
         self.switches = {}
         self.hosts = {}
+        self.links = {}
         super( NetworkDriver, self ).__init__()
+        self.graph = Graph()
 
     def checkOptions( self, var, defaultVar ):
         if var is None or var == "":
@@ -261,17 +264,81 @@
             main.log.error( self.name + ": failed to disconnect inband hosts" )
             return main.FALSE
 
-    def getSwitches( self ):
+    def getSwitches( self, timeout=60, excludeNodes=[], includeStopped=False ):
         """
-        Return a dictionary which maps short names to switch component
+        Return a dictionary which maps short names to switch data
+        If includeStopped is True, stopped switches will also be included
         """
-        return self.switches
+        switches = {}
+        for switchName, switchComponent in self.switches.items():
+            if switchName in excludeNodes:
+                continue
+            if not includeStopped and not switchComponent.isup:
+                continue
+            dpid = switchComponent.dpid.replace( '0x', '' ).zfill( 16 )
+            ports = switchComponent.ports
+            swClass = 'Unknown'
+            pid = None
+            options = None
+            switches[ switchName ] = { "dpid": dpid,
+                                       "ports": ports,
+                                       "swClass": swClass,
+                                       "pid": pid,
+                                       "options": options }
+        return switches
 
-    def getHosts( self ):
+    def getHosts( self, hostClass=None ):
         """
-        Return a dictionary which maps short names to host component
+        Return a dictionary which maps short names to host data
         """
-        return self.hosts
+        hosts = {}
+        for hostName, hostComponent in self.hosts.items():
+            interfaces = hostComponent.interfaces
+            hosts[ hostName ] = { "interfaces": interfaces }
+        return hosts
+
+    def updateLinks( self, timeout=60, excludeNodes=[] ):
+        """
+        Update self.links by getting up-to-date port information from
+        switches
+        """
+        # TODO: also inlcude switch-to-host links
+        self.links = {}
+        for node1 in self.switches.keys():
+            if node1 in excludeNodes:
+                continue
+            self.links[ node1 ] = {}
+            self.switches[ node1 ].updatePorts()
+            for port in self.switches[ node1 ].ports:
+                if not port[ 'enabled' ]:
+                    continue
+                node2 = getattr( main, port[ 'node2' ] ).shortName
+                if node2 in excludeNodes:
+                    continue
+                port1 = port[ 'of_port' ]
+                port2 = port[ 'port2' ]
+                if not self.links[ node1 ].get( node2 ):
+                    self.links[ node1 ][ node2 ] = {}
+                # Check if this link already exists
+                if self.links.get( node2 ):
+                    if self.links[ node2 ].get( node1 ):
+                        if self.links[ node2 ].get( node1 ).get( port2 ):
+                            assert self.links[ node2 ][ node1 ][ port2 ] == port1
+                            continue
+                self.links[ node1 ][ node2 ][ port1 ] = port2
+
+    def getLinks( self, timeout=60, excludeNodes=[] ):
+        """
+        Return a list of links specify both node names and port numbers
+        """
+        self.updateLinks( timeout=timeout, excludeNodes=excludeNodes )
+        links = []
+        for node1, nodeLinks in self.links.items():
+            for node2, ports in nodeLinks.items():
+                for port1, port2 in ports.items():
+                    links.append( { 'node1': node1, 'node2': node2,
+                                    'port1': port1, 'port2': port2 } )
+        return links
 
     def getMacAddress( self, host ):
         """
@@ -279,8 +346,7 @@
         """
         import re
         try:
-            hosts = self.getHosts()
-            hostComponent = hosts[ host ]
+            hostComponent = self.hosts[ host ]
             response = hostComponent.ifconfig()
             pattern = r'HWaddr\s([0-9A-F]{2}[:-]){5}([0-9A-F]{2})'
             macAddressSearch = re.search( pattern, response, re.I )
@@ -297,8 +363,7 @@
             hostName: name of the host e.g. "h1"
             cmd: command to run on the host
         """
-        hosts = self.getHosts()
-        hostComponent = hosts[ hostName ]
+        hostComponent = self.hosts[ hostName ]
         if hostComponent:
             return hostComponent.command( cmd )
         return None
@@ -404,8 +469,7 @@
             returnValue = main.TRUE
             ipv6 = True if protocol == "IPv6" else False
             startTime = time.time()
-            hosts = self.getHosts()
-            hostPairs = itertools.permutations( list( hosts.values() ), 2 )
+            hostPairs = itertools.permutations( list( self.hosts.values() ), 2 )
             for hostPair in list( hostPairs ):
                 ipDst = hostPair[ 1 ].options[ 'ip6' ] if ipv6 else hostPair[ 1 ].options[ 'ip' ]
                 pingResult = hostPair[ 0 ].ping( ipDst, ipv6=ipv6 )
@@ -446,9 +510,8 @@
         import time
         import itertools
         hostComponentList = []
-        hosts = self.getHosts()
         for hostName in hostList:
-            hostComponent = hosts[ hostName ]
+            hostComponent = self.hosts[ hostName ]
             if hostComponent:
                 hostComponentList.append( hostComponent )
         try:
@@ -504,10 +567,9 @@
             main.FALSE otherwise
         """
         try:
-            hosts = self.getHosts()
             if not hostList:
-                hostList = hosts.keys()
-            for hostName, hostComponent in hosts.items():
+                hostList = self.hosts.keys()
+            for hostName, hostComponent in self.hosts.items():
                 if hostName not in hostList:
                     continue
                 ipList = []
@@ -528,7 +590,7 @@
                         hostList.remove( hostName )
             return main.FALSE if hostList else main.TRUE
         except KeyError:
-            main.log.exception( self.name + ": host data not as expected: " + hosts )
+            main.log.exception( self.name + ": host data not as expected: " + self.hosts.keys() )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -584,3 +646,272 @@
             " is " +
             ipAddressSearch.group( 1 ) )
         return ipAddressSearch.group( 1 )
+
+    def getLinkRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipLinks=[] ):
+        """
+        Randomly get a link from network topology.
+        If nonCut is True, it gets a list of non-cut links (the deletion
+        of a non-cut link will not increase the number of connected
+        component of a graph) and randomly returns one of them, otherwise
+        it just randomly returns one link from all current links.
+        excludeNodes will be passed to getLinks and getGraphDict method.
+        Any link that has either end included in skipLinks will be excluded.
+        Returns the link as a list, e.g. [ 's1', 's2' ].
+        """
+        import random
+        candidateLinks = []
+        try:
+            if not nonCut:
+                links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
+                assert len( links ) != 0
+                for link in links:
+                    # Exclude host-switch link
+                    if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
+                        continue
+                    candidateLinks.append( [ link[ 'node1' ], link[ 'node2' ] ] )
+            else:
+                graphDict = self.getGraphDict( timeout=timeout, useId=False,
+                                               excludeNodes=excludeNodes )
+                if graphDict is None:
+                    return None
+                self.graph.update( graphDict )
+                candidateLinks = self.graph.getNonCutEdges()
+            candidateLinks = [ link for link in candidateLinks
+                               if link[0] not in skipLinks and link[1] not in skipLinks ]
+            if candidateLinks is None:
+                return None
+            elif len( candidateLinks ) == 0:
+                main.log.info( self.name + ": No candidate link for deletion" )
+                return None
+            else:
+                link = random.sample( candidateLinks, 1 )
+                return link[ 0 ]
+        except KeyError:
+            main.log.exception( self.name + ": KeyError exception found" )
+            return None
+        except AssertionError:
+            main.log.exception( self.name + ": AssertionError exception found" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
+    def getSwitchRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipSwitches=[] ):
+        """
+        Randomly get a switch from network topology.
+        If nonCut is True, it gets a list of non-cut switches (the deletion
+        of a non-cut switch will not increase the number of connected
+        components of a graph) and randomly returns one of them, otherwise
+        it just randomly returns one switch from all current switches in
+        Mininet.
+        excludeNodes will be pased to getSwitches and getGraphDict method.
+        Switches specified in skipSwitches will be excluded.
+        Returns the name of the chosen switch.
+        """
+        import random
+        candidateSwitches = []
+        try:
+            if not nonCut:
+                switches = self.getSwitches( timeout=timeout, excludeNodes=excludeNodes )
+                assert len( switches ) != 0
+                for switchName in switches.keys():
+                    candidateSwitches.append( switchName )
+            else:
+                graphDict = self.getGraphDict( timeout=timeout, useId=False,
+                                               excludeNodes=excludeNodes )
+                if graphDict is None:
+                    return None
+                self.graph.update( graphDict )
+                candidateSwitches = self.graph.getNonCutVertices()
+            candidateSwitches = [ switch for switch in candidateSwitches if switch not in skipSwitches ]
+            if candidateSwitches is None:
+                return None
+            elif len( candidateSwitches ) == 0:
+                main.log.info( self.name + ": No candidate switch for deletion" )
+                return None
+            else:
+                switch = random.sample( candidateSwitches, 1 )
+                return switch[ 0 ]
+        except KeyError:
+            main.log.exception( self.name + ": KeyError exception found" )
+            return None
+        except AssertionError:
+            main.log.exception( self.name + ": AssertionError exception found" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
+    def getGraphDict( self, timeout=60, useId=True, includeHost=False,
+                      excludeNodes=[] ):
+        """
+        Return a dictionary which describes the latest network topology data as a
+        graph.
+        An example of the dictionary:
+        { 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': ..., 'weight': ... },
+                   vertex3: { 'port': ..., 'weight': ... } }
+        If useId == True, dpid/mac will be used instead of names to identify
+        vertices, which is helpful when e.g. comparing network topology with ONOS
+        topology.
+        If includeHost == True, all hosts (and host-switch links) will be included
+        in topology data.
+        excludeNodes will be passed to getSwitches and getLinks methods to exclude
+        unexpected switches and links.
+        """
+        # TODO: support excludeNodes
+        graphDict = {}
+        try:
+            links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
+            portDict = {}
+            switches = self.getSwitches( excludeNodes=excludeNodes )
+            if includeHost:
+                hosts = self.getHosts()
+            for link in links:
+                # TODO: support 'includeHost' argument
+                if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
+                    continue
+                nodeName1 = link[ 'node1' ]
+                nodeName2 = link[ 'node2' ]
+                if not self.switches[ nodeName1 ].isup or not self.switches[ nodeName2 ].isup:
+                    continue
+                port1 = link[ 'port1' ]
+                port2 = link[ 'port2' ]
+                # Loop for two nodes
+                for i in range( 2 ):
+                    portIndex = port1
+                    if useId:
+                        node1 = 'of:' + str( switches[ nodeName1 ][ 'dpid' ] )
+                        node2 = 'of:' + str( switches[ nodeName2 ][ 'dpid' ] )
+                    else:
+                        node1 = nodeName1
+                        node2 = nodeName2
+                    if node1 not in graphDict.keys():
+                        if useId:
+                            graphDict[ node1 ] = { 'edges': {},
+                                                   'dpid': switches[ nodeName1 ][ 'dpid' ],
+                                                   'name': nodeName1,
+                                                   'ports': switches[ nodeName1 ][ 'ports' ],
+                                                   'swClass': switches[ nodeName1 ][ 'swClass' ],
+                                                   'pid': switches[ nodeName1 ][ 'pid' ],
+                                                   'options': switches[ nodeName1 ][ 'options' ] }
+                        else:
+                            graphDict[ node1 ] = { 'edges': {} }
+                    else:
+                        # Assert node2 is not connected to any current links of node1
+                        # assert node2 not in graphDict[ node1 ][ 'edges' ].keys()
+                        pass
+                    for port in switches[ nodeName1 ][ 'ports' ]:
+                        if port[ 'of_port' ] == str( portIndex ):
+                            # Use -1 as index for disabled port
+                            if port[ 'enabled' ]:
+                                graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': portIndex }
+                            else:
+                                graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': -1 }
+                    # Swap two nodes/ports
+                    nodeName1, nodeName2 = nodeName2, nodeName1
+                    port1, port2 = port2, port1
+            # Remove links with disabled ports
+            linksToRemove = []
+            for node, edges in graphDict.items():
+                for neighbor, port in edges[ 'edges' ].items():
+                    if port[ 'port' ] == -1:
+                        linksToRemove.append( ( node, neighbor ) )
+            for node1, node2 in linksToRemove:
+                for i in range( 2 ):
+                    if graphDict.get( node1 )[ 'edges' ].get( node2 ):
+                        graphDict[ node1 ][ 'edges' ].pop( node2 )
+                    node1, node2 = node2, node1
+            return graphDict
+        except KeyError:
+            main.log.exception( self.name + ": KeyError exception found" )
+            return None
+        except AssertionError:
+            main.log.exception( self.name + ": AssertionError exception found" )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
+    def switch( self, **switchargs ):
+        """
+        start/stop a switch
+        """
+        args = utilities.parse_args( [ "SW", "OPTION" ], **switchargs )
+        sw = args[ "SW" ] if args[ "SW" ] is not None else ""
+        option = args[ "OPTION" ] if args[ "OPTION" ] is not None else ""
+        try:
+            switchComponent = self.switches[ sw ]
+            if option == 'stop':
+                switchComponent.stopOfAgent()
+            elif option == 'start':
+                switchComponent.startOfAgent()
+            else:
+                main.log.warn( self.name + ": Unknown switch command" )
+                return main.FALSE
+            return main.TRUE
+        except KeyError:
+            main.log.error( self.name + ": Not able to find switch [}".format( sw ) )
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": TIMEOUT exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            main.cleanAndExit()
+
+    def discoverHosts( self, hostList=[], wait=1000, dstIp="6.6.6.6", dstIp6="1020::3fe" ):
+        '''
+        Hosts in hostList will do a single ARP/ND to a non-existent address for ONOS to
+        discover them. A host will use arping/ndisc6 to send ARP/ND depending on if it
+        has IPv4/IPv6 addresses configured.
+        Optional:
+            hostList: a list of names of the hosts that need to be discovered. If not
+                      specified mininet will send ping from all the hosts
+            wait: timeout for ARP/ND in milliseconds
+            dstIp: destination address used by IPv4 hosts
+            dstIp6: destination address used by IPv6 hosts
+        Returns:
+            main.TRUE if all packets were successfully sent. Otherwise main.FALSE
+        '''
+        try:
+            hosts = self.getHosts()
+            if not hostList:
+                hostList = hosts.keys()
+            discoveryResult = main.TRUE
+            for host in hostList:
+                flushCmd = ""
+                cmd = ""
+                if self.getIPAddress( host ):
+                    flushCmd = "sudo ip neigh flush all"
+                    cmd = "arping -c 1 -w {} {}".format( wait, dstIp )
+                    main.log.debug( "Sending IPv4 arping from host {}".format( host ) )
+                elif self.getIPAddress( host, proto='IPV6' ):
+                    flushCmd = "sudo ip -6 neigh flush all"
+                    intf = hosts[host]['interfaces'][0]['name']
+                    cmd = "ndisc6 -r 1 -w {} {} {}".format( wait, dstIp6, intf )
+                    main.log.debug( "Sending IPv6 ND from host {}".format( host ) )
+                else:
+                    main.log.warn( "No IP addresses configured on host {}, skipping discovery".format( host ) )
+                    discoveryResult = main.FALSE
+                if cmd:
+                    self.runCmdOnHost( host, flushCmd )
+                    self.runCmdOnHost( host, cmd )
+            return discoveryResult
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
diff --git a/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py b/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
index ac909ee..08f691a 100644
--- a/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
+++ b/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
@@ -38,6 +38,7 @@
         """
         super( CLI, self ).__init__()
         self.name = None
+        self.shortName = None
         self.handle = None
         self.prompt = "~#"
         # Respect to bin folder
@@ -46,6 +47,8 @@
         self.tempDirectory = "/tmp/"
         self.conf = "ofagent.conf"
         self.switchDirectory = "/etc/ofagent/"
+        self.ports = []
+        self.isup = False
 
     def connect( self, **connectargs ):
         """
@@ -57,9 +60,19 @@
                 vars( self )[ key ] = connectargs[ key ]
             # Get the name
             self.name = self.options[ 'name' ]
+            self.shortName = self.options[ 'shortName' ]
             # Get the dpid
             self.dpid = self.options[ 'dpid' ]
             # Get ofagent patch
+            for key, value in self.options.items():
+                if re.match( 'link[\d]+', key ):
+                    self.ports.append( { 'enabled': True,
+                                         'ips': [ None ],
+                                         'mac': None,
+                                         'name': None,
+                                         'node2': value[ 'node2' ],
+                                         'port2': value[ 'port2' ],
+                                         'of_port': value[ 'port1' ] } )
             if 'confDir' in self.options:
                 self.switchDirectory = self.options[ 'confDir' ]
             # Parse the IP address
@@ -165,7 +178,7 @@
                 onosIp = "-t " + str( ip )
             elif isinstance( ip, types.ListType ):
                 for ipAddress in ip:
-                        onosIp += "-t " + str( ipAddress ) + " "
+                    onosIp += "-t " + str( ipAddress ) + " "
             else:
                 main.log.error( self.name + ": Invalid ip address" )
                 return main.FALSE
@@ -189,6 +202,10 @@
                 kwargs={},
                 attempts=10,
                 sleep=10)
+            if not assignResult:
+                self.isup = False
+            else:
+                self.isup = True
             # Done return true
             return assignResult
         # Errors handling
@@ -221,10 +238,21 @@
         """
         Create a backup file of the old configuration on the switch
         """
-        self.handle.sendline( "" )
-        self.handle.expect( self.prompt )
-        self.handle.sendline( "cp %s%s %s%s.backup" % (self.switchDirectory, self.conf, self.switchDirectory, self.conf) )
-        self.handle.expect( self.prompt )
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "cp %s%s %s%s.backup" % (self.switchDirectory, self.conf, self.switchDirectory, self.conf) )
+            self.handle.expect( self.prompt )
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
 
     def pushCfg( self ):
         """
@@ -236,23 +264,75 @@
         os.system( "scp " + self.tempDirectory + self.conf + " " +
                    self.user_name + "@" + self.ip_address + ":" + self.switchDirectory)
 
+    def ofagentIsRunning( self ):
+        """
+        Return main.TRUE if service ofagentd is running on the
+        switch; otherwise main.FALSE
+        """
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "service ofagentd status" )
+            self.handle.expect( self.prompt )
+            response = self.handle.before
+            if "ofagentd is running" in response:
+                return main.TRUE
+            else:
+                return main.FALSE
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
     def startOfAgent( self ):
         """
         Start the ofagent on the device
         """
-        self.handle.sendline( "" )
-        self.handle.expect( self.prompt )
-        self.handle.sendline( "service ofagentd start" )
-        self.handle.expect( self.prompt )
+        try:
+            if self.ofagentIsRunning():
+                main.log.warn( self.name + ": ofagentd is already running" )
+                return main.TRUE
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "service ofagentd start" )
+            self.handle.expect( self.prompt )
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
 
     def stopOfAgent( self ):
         """
         Stop the ofagent on the device
         """
-        self.handle.sendline( "" )
-        self.handle.expect( self.prompt )
-        self.handle.sendline( "service ofagentd stop" )
-        self.handle.expect( self.prompt )
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "service ofagentd stop" )
+            self.handle.expect( self.prompt )
+            self.isup = False
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
 
     def dumpFlows( self ):
         """
@@ -318,19 +398,89 @@
         Enable all the ports on the devices
         It needs to wait for the boot
         """
-        self.handle.sendline( "" )
-        self.handle.expect( self.prompt )
-        self.handle.sendline( "client_port_table_dump" )
-        self.handle.expect( self.prompt )
-        response = self.handle.before
-        if "Error from ofdpaClientInitialize()" in response:
-            main.log.warn( self.name + ": Not yet started" )
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "client_port_table_dump" )
+            self.handle.expect( self.prompt )
+            response = self.handle.before
+            if "Error from ofdpaClientInitialize()" in response:
+                main.log.warn( self.name + ": Not yet started" )
+                return main.FALSE
+            # Change port speed
+            self.handle.sendline( "sh portspeed" )
+            self.handle.expect( self.prompt )
+            response = self.handle.before
+            if "Failure calling" in response:
+                main.log.warn( self.name + ": failed to change port speed" )
+                return main.FALSE
+            return main.TRUE
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
             return main.FALSE
-        # Change port speed
-        self.handle.sendline( "sh portspeed" )
-        self.handle.expect( self.prompt )
-        response = self.handle.before
-        if "Failure calling" in response:
-            main.log.warn( self.name + ": failed to change port speed" )
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def setPortSpeed( self, index, speed=40000 ):
+        """
+        Run client_drivshell on the switch to set speed for a
+        specific port
+        index: port index, e.g. 1
+        speed: port speed, e.g. 40000
+        """
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            cmd = "client_drivshell port {} sp={}".format( index, speed )
+            self.handle.sendline( cmd )
+            self.handle.expect( self.prompt )
+            response = self.handle.before
+            return main.TRUE
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
             return main.FALSE
-        return main.TRUE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def updatePorts( self ):
+        """
+        Get latest port status on the switch by running
+        client_port_table_dump commmand and parsing the output
+        """
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( "client_port_table_dump" )
+            self.handle.expect( self.prompt )
+            ports = self.handle.before
+            if "Error from ofdpaClientInitialize()" in ports:
+                main.log.warn( self.name + ": Not yet started" )
+                return main.FALSE
+            ports = re.findall( r"0x[\d]+.*port[\d]+:\r\r\n.*\r\r\n.*PeerFeature:.*\r\r\n", ports )
+            for port in ports:
+                m = re.match( r".*port([\d]+):\r\r\n.*state = (.*), mac", port )
+                index = m.group( 1 )
+                enabled = True if m.group( 2 ) == '0x00000000' else False
+                for p in self.ports:
+                    if p[ 'of_port' ] == index:
+                        p[ 'enabled' ] = enabled
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": pexpect.TIMEOUT found" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
index 1452741..b45d70f 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
@@ -34,6 +34,7 @@
     <TEST>
         <topo>1</topo>
         <IPv6>on</IPv6>
+        <restartCluster>True</restartCluster>
         <dataPlaneConnectivity>False</dataPlaneConnectivity>
         <numCtrl>3</numCtrl>
         <pauseTest>off</pauseTest>
@@ -407,7 +408,7 @@
         </topo4>
         <mininetArgs></mininetArgs>
         <loadTopoSleep>5</loadTopoSleep>
-        <excludeSwitches></excludeSwitches>
+        <excludeNodes></excludeNodes>
     </TOPO>
 
     <CASE1>
@@ -435,13 +436,16 @@
 
     <CASE70>
         <sleepSec>2</sleepSec>
-        <addHostIntentWeight>3</addHostIntentWeight>
-        <addPointIntentWeight>3</addPointIntentWeight>
-        <linkDownWeight>3</linkDownWeight>
-        <deviceDownWeight>2</deviceDownWeight>
-        <portDownWeight>0</portDownWeight>
-        <onosDownWeight>1</onosDownWeight>
-        <toggleFlowObj>0</toggleFlowObj>
+        <eventWeight>
+            <add-host-intent>3</add-host-intent>
+            <add-point-intent>3</add-point-intent>
+            <link-down>3</link-down>
+            <device-down>2</device-down>
+            <onos-down>1</onos-down>
+            <toggle-flowobj>0</toggle-flowobj>
+        </eventWeight>
+        <skipSwitches></skipSwitches>
+        <skipLinks></skipLinks>
     </CASE70>
 
     <CASE80>
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis
index f6aa628..ae54287 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis
@@ -12,7 +12,7 @@
     # 100. Do nothing
 
     <testcases>
-        0,2,4,5,6,7,10,70,100
+        0,2,4,5,6,7,10,70
     </testcases>
 
     <GIT>
@@ -23,6 +23,7 @@
     <TEST>
         <topo>10</topo>
         <IPv6>on</IPv6>
+        <restartCluster>True</restartCluster>
         <dataPlaneConnectivity>True</dataPlaneConnectivity>
         <numCtrl>3</numCtrl>
         <pauseTest>on</pauseTest>
@@ -52,7 +53,7 @@
             <CLI>null</CLI>
             <CLIParamNum>0</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </Event>
 
         <TestPause>
@@ -87,28 +88,6 @@
             <CLIParamNum>0</CLIParamNum>
         </TestDebug>
 
-        <IntentCheck>
-            <status>off</status>
-            <typeIndex>10</typeIndex>
-            <typeString>CHECK_INTENT</typeString>
-            <CLI>check-intent</CLI>
-            <CLIParamNum>0</CLIParamNum>
-            <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
-        </IntentCheck>
-
-        <FlowCheck>
-            <status>off</status>
-            <typeIndex>11</typeIndex>
-            <typeString>CHECK_FLOW</typeString>
-            <CLI>check-flow</CLI>
-            <CLIParamNum>0</CLIParamNum>
-            <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
-            <coreFlowNum>3</coreFlowNum>
-            <coreFlowNum6>5</coreFlowNum6>
-        </FlowCheck>
-
         <TrafficCheck>
             <status>on</status>
             <typeIndex>12</typeIndex>
@@ -116,7 +95,7 @@
             <CLI>check-traffic</CLI>
             <CLIParamNum>0</CLIParamNum>
             <rerunInterval>10</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
             <pingWait>1</pingWait>
             <pingTimeout>10</pingTimeout>
         </TrafficCheck>
@@ -128,7 +107,7 @@
             <CLI>check-topo</CLI>
             <CLIParamNum>0</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </TopoCheck>
 
         <ONOSCheck>
@@ -138,7 +117,7 @@
             <CLI>check-onos</CLI>
             <CLIParamNum>0</CLIParamNum>
             <rerunInterval>10</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </ONOSCheck>
 
         <RaftLogSizeCheck>
@@ -204,7 +183,7 @@
             <CLI>onos-down</CLI>
             <CLIParamNum>1</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </ONOSDown>
 
         <ONOSUp>
@@ -214,7 +193,7 @@
             <CLI>onos-up</CLI>
             <CLIParamNum>1</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </ONOSUp>
 
         <SetCfg>
@@ -224,7 +203,7 @@
             <CLI>set-cfg</CLI>
             <CLIParamNum>3</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </SetCfg>
 
         <BalanceMasters>
@@ -234,7 +213,7 @@
             <CLI>balance-masters</CLI>
             <CLIParamNum>0</CLIParamNum>
             <rerunInterval>5</rerunInterval>
-            <maxRerunNum>5</maxRerunNum>
+            <maxRerunNum>3</maxRerunNum>
         </BalanceMasters>
 
         <addAllChecks>
@@ -244,51 +223,6 @@
             <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>
-
-        <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>
@@ -308,7 +242,7 @@
         </topo10>
         <mininetArgs>--dhcp=1 --routers=1 --ipv6=1 --ipv4=1</mininetArgs>
         <loadTopoSleep>120</loadTopoSleep>
-        <excludeSwitches>leaf5</excludeSwitches>
+        <excludeNodes>cs0,cs1,r1,r2,nat,bgp1,bgp2,dhcp,dhcp6</excludeNodes>
     </TOPO>
 
     <CASE2>
@@ -323,18 +257,18 @@
 
     <CASE70>
         <sleepSec>60</sleepSec>
-        <addHostIntentWeight>0</addHostIntentWeight>
-        <addPointIntentWeight>0</addPointIntentWeight>
-        <linkDownWeight>0</linkDownWeight>
-        <deviceDownWeight>2</deviceDownWeight>
-        <portDownWeight>3</portDownWeight>
-        <onosDownWeight>1</onosDownWeight>
-        <toggleFlowObj>0</toggleFlowObj>
+        <eventWeight>
+            <port-down>3</port-down>
+            <device-down>2</device-down>
+            <onos-down>1</onos-down>
+        </eventWeight>
+        <skipSwitches>leaf5</skipSwitches>
+        <skipLinks>spine103,spine104,leaf1,leaf6</skipLinks>
     </CASE70>
 
     <CASE80>
         <filePath>/home/sdn/log-for-replay</filePath>
-        <sleepTime>10</sleepTime>
+        <sleepTime>5</sleepTime>
         <skipChecks>on</skipChecks>
     </CASE80>
 </PARAMS>
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis.flex b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis.flex
new file mode 100644
index 0000000..9f2f22b
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params.trellis.flex
@@ -0,0 +1,248 @@
+<PARAMS>
+    # 0. Initialize CHOTestMonkey
+    # 1. Set IPv6 configure
+    # 2. Load network configuration files
+    # 4. Copy topology libs and config files to Mininet
+    # 5. Load topology and balances all switches
+    # 6. Collect and store device and link data from ONOS
+    # 7. Collect and store host data from ONOS
+    # 10. Run all enabled checks
+    # 70. Run randomly generated events
+    # 80. Replay events from log file
+    # 100. Do nothing
+
+    <testcases>
+        0,2,5,6,7,10,70
+    </testcases>
+
+    <GIT>
+        <pull>False</pull>
+        <branch>master</branch>
+    </GIT>
+
+    <TEST>
+        <IPv6>off</IPv6>
+        <restartCluster>False</restartCluster>
+        <dataPlaneConnectivity>True</dataPlaneConnectivity>
+        <numCtrl>3</numCtrl>
+        <pauseTest>on</pauseTest>
+        <caseSleep>0</caseSleep>
+        <ipv6Regex>10[0-9]+::[0-9]+</ipv6Regex>
+        <ipv4Regex>10\.0\.[0-9]+\.[0-9]+</ipv4Regex>
+        <karafCliTimeout>7200000</karafCliTimeout>
+        <testDuration>86400</testDuration>
+        <package>on</package>
+    </TEST>
+
+    <GRAPH>
+        <nodeCluster>CHO</nodeCluster>
+        <builds>20</builds>
+    </GRAPH>
+
+    <ENV>
+        <cellName>choFlexCell</cellName>
+        <cellApps>drivers,openflow,segmentrouting,fpm,dhcprelay,netcfghostprovider,routeradvertisement,t3,hostprobingprovider</cellApps>
+    </ENV>
+
+    <EVENT>
+        <Event>
+            <status>on</status>
+            <typeIndex>0</typeIndex>
+            <typeString>NULL</typeString>
+            <CLI>null</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>3</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>
+
+        <TestDebug>
+            <status>on</status>
+            <typeIndex>4</typeIndex>
+            <typeString>TEST_DEBUG</typeString>
+            <CLI>debug-test</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </TestDebug>
+
+        <TrafficCheck>
+            <status>on</status>
+            <typeIndex>12</typeIndex>
+            <typeString>CHECK_TRAFFIC</typeString>
+            <CLI>check-traffic</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>10</rerunInterval>
+            <maxRerunNum>3</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>3</maxRerunNum>
+        </TopoCheck>
+
+        <ONOSCheck>
+            <status>on</status>
+            <typeIndex>14</typeIndex>
+            <typeString>CHECK_ONOS</typeString>
+            <CLI>check-onos</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>10</rerunInterval>
+            <maxRerunNum>3</maxRerunNum>
+        </ONOSCheck>
+
+        <RaftLogSizeCheck>
+            <status>on</status>
+            <typeIndex>15</typeIndex>
+            <typeString>CHECK_RAFT_LOG_SIZE</typeString>
+            <CLI>check-raft-size</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </RaftLogSizeCheck>
+
+        <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>
+
+        <PortDown>
+            <status>on</status>
+            <typeIndex>24</typeIndex>
+            <typeString>NETWORK_PORT_DOWN</typeString>
+            <CLI>port-down</CLI>
+            <CLIParamNum>2</CLIParamNum>
+        </PortDown>
+
+        <PortUp>
+            <status>on</status>
+            <typeIndex>25</typeIndex>
+            <typeString>NETWORK_PORT_UP</typeString>
+            <CLI>port-up</CLI>
+            <CLIParamNum>2</CLIParamNum>
+        </PortUp>
+
+        <ONOSDown>
+            <status>on</status>
+            <typeIndex>40</typeIndex>
+            <typeString>ONOS_ONOS_DOWN</typeString>
+            <CLI>onos-down</CLI>
+            <CLIParamNum>1</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>3</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>3</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>3</maxRerunNum>
+        </SetCfg>
+
+        <BalanceMasters>
+            <status>on</status>
+            <typeIndex>44</typeIndex>
+            <typeString>ONOS_BALANCE_MASTERS</typeString>
+            <CLI>balance-masters</CLI>
+            <CLIParamNum>0</CLIParamNum>
+            <rerunInterval>5</rerunInterval>
+            <maxRerunNum>3</maxRerunNum>
+        </BalanceMasters>
+
+        <addAllChecks>
+            <status>on</status>
+            <typeIndex>110</typeIndex>
+            <typeString>CHECK_ALL</typeString>
+            <CLI>check-all</CLI>
+            <CLIParamNum>0</CLIParamNum>
+        </addAllChecks>
+    </EVENT>
+
+    <SCHEDULER>
+        <pendingEventsCapacity>1</pendingEventsCapacity>
+        <runningEventsCapacity>10</runningEventsCapacity>
+        <scheduleLoopSleep>0.1</scheduleLoopSleep>
+    </SCHEDULER>
+
+    <GENERATOR>
+        <listenerPort>6000</listenerPort>
+        <insertEventRetryInterval>1</insertEventRetryInterval>
+    </GENERATOR>
+
+    <TOPO>
+        <loadTopoSleep>60</loadTopoSleep>
+        <excludeNodes></excludeNodes>
+    </TOPO>
+
+    <CASE2>
+        <fileName>flex.json</fileName>
+        <hostFileName>flex.host</hostFileName>
+    </CASE2>
+
+    <CASE70>
+        <sleepSec>60</sleepSec>
+        <eventWeight>
+            <port-down>2</port-down>
+            <device-down>2</device-down>
+            <onos-down>1</onos-down>
+        </eventWeight>
+        <skipSwitches>s201,s228</skipSwitches>
+        <skipLinks></skipLinks>
+    </CASE70>
+
+    <CASE80>
+        <filePath>/home/sdn/log-for-replay</filePath>
+        <sleepTime>5</sleepTime>
+        <skipChecks>off</skipChecks>
+    </CASE80>
+</PARAMS>
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
index 000dfe3..bc6f249 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
@@ -45,7 +45,6 @@
         onos-uninstall
         onos-install
         onos-start-cli
-        Set IPv6 cfg parameters for Neighbor Discovery
         start event scheduler
         start event listener
         """
@@ -64,6 +63,9 @@
             main.cleanAndExit()
         main.testSetUp.envSetupDescription()
 
+        from tests.dependencies.Network import Network
+        main.Network = Network()
+
         try:
             onosPackage = main.params[ 'TEST' ][ 'package' ]
             karafTimeout = main.params[ 'TEST' ][ 'karafCliTimeout' ]
@@ -71,12 +73,19 @@
             main.caseSleep = int( main.params[ 'TEST' ][ 'caseSleep' ] )
             main.onosCell = main.params[ 'ENV' ][ 'cellName' ]
             main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+            main.restartCluster = main.params[ 'TEST' ][ 'restartCluster' ] == "True"
+            main.excludeNodes = main.params[ 'TOPO' ][ 'excludeNodes' ].split( ',' ) \
+                                if main.params[ 'TOPO' ][ 'excludeNodes' ] else []
+            main.skipSwitches = main.params[ 'CASE70' ][ 'skipSwitches' ].split( ',' ) \
+                                if main.params[ 'CASE70' ][ 'skipSwitches' ] else []
+            main.skipLinks = main.params[ 'CASE70' ][ 'skipLinks' ].split( ',' ) \
+                             if main.params[ 'CASE70' ][ 'skipLinks' ] else []
             main.controllers = []
-
             main.devices = []
             main.links = []
             main.hosts = []
             main.intents = []
+            main.flowObj = False
             main.enabledEvents = {}
             for eventName in main.params[ 'EVENT' ].keys():
                 if main.params[ 'EVENT' ][ eventName ][ 'status' ] == 'on':
@@ -85,7 +94,7 @@
             main.eventScheduler = EventScheduler()
             main.eventGenerator = EventGenerator()
             main.variableLock = Lock()
-            main.mininetLock = Lock()
+            main.networkLock = Lock()
             main.ONOSbenchLock = Lock()
             main.threadID = 0
             main.eventID = 0
@@ -97,7 +106,8 @@
         main.testSetUp.evnSetupConclusion( stepResult )
 
         setupResult = main.testSetUp.ONOSSetUp( main.Cluster,
-                                                cellName=main.onosCell )
+                                                cellName=main.onosCell,
+                                                restartCluster=main.restartCluster )
         for i in range( 1, main.Cluster.numCtrls + 1 ):
             newController = Controller( i )
             newController.setCLI( main.Cluster.runningNodes[ i - 1 ].CLI )
@@ -225,71 +235,92 @@
 
     def CASE5( self, main ):
         """
-        Load Mininet topology and balances all switches
+        Load Mininet or physical network topology and balances switch mastership
         """
         import time
         import re
-        main.log.report( "Load Mininet topology and Balance all Mininet switches across controllers" )
+        main.log.report( "Load Mininet or physical network topology and Balance switch mastership" )
         main.log.report( "________________________________________________________________________" )
-        main.case( "Assign and Balance all Mininet switches across controllers" )
+        main.case( "Load Mininet or physical network topology and Balance switch mastership" )
 
-        main.step( "Copy Mininet topology files" )
-        main.topoIndex = "topo" + str( main.params[ 'TEST' ][ 'topo' ] )
-        topoFileName = main.params[ 'TOPO' ][ main.topoIndex ][ 'fileName' ]
-        topoFile = main.testDir + "/dependencies/topologies/" + topoFileName
-        copyResult = main.ONOSbench.scp( main.Mininet1, topoFile, main.Mininet1.home + "/custom", direction="to" )
-        utilities.assert_equals( expect=main.TRUE,
-                                 actual=copyResult,
-                                 onpass="Successfully copied topo files",
-                                 onfail="Failed to copy topo files" )
+        if hasattr( main, 'Mininet1' ):
+            main.step( "Copy Mininet topology files" )
+            main.topoIndex = "topo" + str( main.params[ 'TEST' ][ 'topo' ] )
+            topoFileName = main.params[ 'TOPO' ][ main.topoIndex ][ 'fileName' ]
+            topoFile = main.testDir + "/dependencies/topologies/" + topoFileName
+            copyResult = main.ONOSbench.scp( main.Mininet1, topoFile, main.Mininet1.home + "/custom", direction="to" )
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=copyResult,
+                                     onpass="Successfully copied topo files",
+                                     onfail="Failed to copy topo files" )
 
-        main.step( "Start Mininet topology" )
-        startStatus = main.Mininet1.startNet( topoFile=main.Mininet1.home + "/custom/" + topoFileName,
-                                              args=main.params[ 'TOPO' ][ 'mininetArgs' ] )
-        main.mininetSwitches = main.Mininet1.getSwitches( switchClasses=r"(OVSSwitch)" )
+        main.step( "Load topology" )
+        if hasattr( main, 'Mininet1' ):
+            topoResult = main.Mininet1.startNet( topoFile=main.Mininet1.home + "/custom/" + topoFileName,
+                                                  args=main.params[ 'TOPO' ][ 'mininetArgs' ] )
+        else:
+            topoResult = main.NetworkBench.connectToNet()
         utilities.assert_equals( expect=main.TRUE,
-                                 actual=startStatus,
-                                 onpass="Start Mininet topology test PASS",
-                                 onfail="Start Mininet topology test FAIL" )
+                                 actual=topoResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanAndExit()
 
         main.step( "Assign switches to controllers" )
-        switchMastership = main.TRUE
-        for switchName in main.mininetSwitches.keys():
+        if hasattr( main, 'Mininet1' ):
+            main.networkSwitches = main.Network.getSwitches( excludeNodes=main.excludeNodes )
+        else:
+            main.networkSwitches = main.Network.getSwitches( excludeNodes=main.excludeNodes,
+                                                             includeStopped=True )
+        assignResult = main.TRUE
+        for name in main.networkSwitches.keys():
             ips = main.Cluster.getIps()
-            main.Mininet1.assignSwController( sw=switchName, ip=ips )
-            response = main.Mininet1.getSwController( switchName )
-            main.log.debug( "Response is " + str( response ) )
-            if re.search( "tcp:" + main.Cluster.active( 0 ).ipAddress, response ):
-                switchMastership = switchMastership and main.TRUE
-            else:
-                switchMastership = main.FALSE
+            assignResult = assignResult and main.Network.assignSwController( sw=name, ip=ips )
         utilities.assert_equals( expect=main.TRUE,
-                                 actual=switchMastership,
-                                 onpass="Assign switches to controllers test PASS",
-                                 onfail="Assign switches to controllers test FAIL" )
+                                 actual=assignResult,
+                                 onpass="Successfully assign switches to controllers",
+                                 onfail="Failed to assign switches to controllers" )
+
         # Waiting here to make sure topology converges across all nodes
         sleep = int( main.params[ 'TOPO' ][ 'loadTopoSleep' ] )
         time.sleep( sleep )
 
         main.step( "Balance devices across controllers" )
         balanceResult = main.Cluster.active( 0 ).CLI.balanceMasters()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=balanceResult,
+                                 onpass="Successfully balanced mastership",
+                                 onfail="Faild to balance mastership" )
         # giving some breathing time for ONOS to complete re-balance
         time.sleep( 5 )
 
-        # Get mininet hosts and links
-        main.mininetHosts = main.Mininet1.getHosts()
-        if hasattr( main, "expectedHosts" ):
-            main.mininetHosts = { key: value for key, value in main.mininetHosts.items() if key in main.expectedHosts[ "network" ].keys() }
-        main.mininetLinks = main.Mininet1.getLinks( timeout=60 )
-        main.mininetLinks = [ link for link in main.mininetLinks if
-                              link[ 'node1' ] in main.mininetHosts.keys() + main.mininetSwitches.keys() and
-                              link[ 'node2' ] in main.mininetHosts.keys() + main.mininetSwitches.keys() ]
+        # Connecting to hosts that only have data plane connectivity
+        if hasattr( main, 'NetworkBench' ):
+            main.step( "Connecting inband hosts" )
+            hostResult = main.NetworkBench.connectInbandHosts()
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=hostResult,
+                                     onpass="Successfully connected inband hosts",
+                                     onfail="Failed to connect inband hosts" )
+        else:
+            hostResult = main.TRUE
 
-        caseResult = ( startStatus and switchMastership and balanceResult )
+        # Get network hosts and links
+        main.networkHosts = main.Network.getHosts()
+        if hasattr( main, "expectedHosts" ):
+            main.networkHosts = { key: value for key, value in main.networkHosts.items() if key in main.expectedHosts[ "network" ].keys() }
+        main.networkLinks = main.Network.getLinks()
+        main.networkLinks = [ link for link in main.networkLinks if
+                              link[ 'node1' ] in main.networkHosts.keys() + main.networkSwitches.keys() and
+                              link[ 'node2' ] in main.networkHosts.keys() + main.networkSwitches.keys() ]
+
+        caseResult = ( topoResult and assignResult and balanceResult and hostResult )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=caseResult,
-                                 onpass="Starting new Att topology test PASS",
-                                 onfail="Starting new Att topology test FAIL" )
+                                 onpass="Starting new topology test PASS",
+                                 onfail="Starting new topology test FAIL" )
 
     def CASE6( self, main ):
         """
@@ -307,13 +338,16 @@
         topologyResult = main.Cluster.active( 0 ).CLI.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:
+        networkSwitchNum = len( main.networkSwitches )
+        if hasattr( main, 'Mininet1' ):
+            networkLinkNum = ( len( main.networkLinks ) - len( main.networkHosts ) ) * 2
+        else:
+            networkLinkNum = len( main.networkLinks ) * 2
+        if networkSwitchNum == ONOSDeviceNum and networkLinkNum == ONOSLinkNum:
             main.step( "Collect and store device data" )
             stepResult = main.TRUE
             dpidToName = {}
-            for key, value in main.mininetSwitches.items():
+            for key, value in main.networkSwitches.items():
                 dpidToName[ 'of:' + str( value[ 'dpid' ] ) ] = key
             main.devicesRaw = main.Cluster.active( 0 ).CLI.devices()
             devices = json.loads( main.devicesRaw )
@@ -364,7 +398,7 @@
                                      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 (expected): %s, Links (expected): %s" % ( networkSwitchNum, networkLinkNum ) )
             main.log.info( "Devices (actual): %s, Links (actual): %s" % ( ONOSDeviceNum, ONOSLinkNum ) )
             topoResult = main.FALSE
 
@@ -404,9 +438,9 @@
                                      onfail="Failed to enable reactive forwarding" )
 
             main.step( "Discover hosts using pingall" )
-            main.Mininet1.pingall()
+            main.Network.pingall()
             if main.enableIPv6:
-                ping6Result = main.Mininet1.pingall( protocol="IPv6" )
+                ping6Result = main.Network.pingall( protocol="IPv6" )
 
             main.step( "Disable Reactive forwarding" )
             appResult = main.Cluster.active( 0 ).CLI.deactivateApp( "org.onosproject.fwd" )
@@ -422,7 +456,7 @@
         hosts = json.loads( main.hostsRaw )
         if hasattr( main, "expectedHosts" ):
             hosts = [ host for host in hosts if host[ 'id' ] in main.expectedHosts[ 'onos' ].keys() ]
-        if not len( hosts ) == len( main.mininetHosts ):
+        if not len( hosts ) == len( main.networkHosts ):
             stepResult = main.FALSE
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -435,7 +469,7 @@
         main.step( "Collect and store host data" )
         stepResult = main.TRUE
         macToName = {}
-        for key, value in main.mininetHosts.items():
+        for key, value in main.networkHosts.items():
             macToName[ value[ 'interfaces' ][ 0 ][ 'mac' ].upper() ] = key
         dpidToDevice = {}
         for device in main.devices:
@@ -472,12 +506,16 @@
                                  onfail="Failed to collect and store host data" )
 
         main.step( "Create one host component for each host and then start host cli" )
-        startCLIResult = main.TRUE
+        hostResult = main.TRUE
         for host in main.hosts:
-            main.Mininet1.createHostComponent( host.name )
+            main.Network.createHostComponent( host.name )
             hostHandle = getattr( main, host.name )
-            main.log.info( "Starting CLI on host " + str( host.name ) )
-            startCLIResult = startCLIResult and hostHandle.startHostCli()
+            if hasattr( main, "Mininet1" ):
+                main.log.info( "Starting CLI on host " + str( host.name ) )
+                hostResult = hostResult and hostHandle.startHostCli()
+            else:
+                main.log.info( "Connecting inband host " + str( host.name ) )
+                hostResult = hostResult and hostHandle.connectInband()
             host.setHandle( hostHandle )
             if main.params[ 'TEST' ][ 'dataPlaneConnectivity' ] == 'True':
                 # Hosts should already be able to ping each other
@@ -486,9 +524,9 @@
                 if host in main.ipv6Hosts:
                     host.correspondents += main.ipv6Hosts
         utilities.assert_equals( expect=main.TRUE,
-                                 actual=startCLIResult,
-                                 onpass="Host CLI started",
-                                 onfail="Failed to start host CLI" )
+                                 actual=hostResult,
+                                 onpass="Host components created",
+                                 onfail="Failed to create host components" )
 
     def CASE10( self, main ):
         """
@@ -808,43 +846,26 @@
         main.step( "Randomly generate events" )
         main.caseResult = main.TRUE
         sleepSec = int( main.params[ 'CASE70' ][ 'sleepSec' ] )
-        hostIntentNum = 0
-        pointIntentNum = 0
-        downDeviceNum = 0
-        downLinkNum = 0
-        downPortNum = 0
-        downOnosNum = 0
-        flowObj = False
-        upControllers = range( 1, int( main.params[ 'TEST' ][ 'numCtrl' ] ) + 1 )
+        allControllers = range( 1, int( main.params[ 'TEST' ][ 'numCtrl' ] ) + 1 )
         while True:
+            upControllers = [ i for i in allControllers if main.controllers[ i - 1 ].isUp() ]
+            downOnosNum = len( allControllers ) - len( upControllers )
+            hostIntentNum = len( [ intent for intent in main.intents if intent.type == 'INTENT_HOST' ] )
+            pointIntentNum = len( [ intent for intent in main.intents if intent.type == 'INTENT_POINT' ] )
+            downDeviceNum = len( [ device for device in main.devices if device.isDown() or device.isRemoved() ] )
+            downLinkNum = len( [ link for link in main.links if link.isDown() ] ) / 2
+            downPortNum = sum( [ len( device.downPorts ) for device in main.devices ] )
             events = []
-            for i in range( int( main.params[ 'CASE70' ][ 'toggleFlowObj' ] ) ):
-                events.append( 'toggle-flowobj' )
-            for i in range( int( main.params[ 'CASE70' ][ 'addHostIntentWeight' ] ) ):
-                events.append( 'add-host-intent' )
-            for i in range( int( main.params[ 'CASE70' ][ 'addPointIntentWeight' ] ) ):
-                events.append( 'add-point-intent' )
-            for i in range( int( main.params[ 'CASE70' ][ 'linkDownWeight' ] ) ):
-                events.append( 'link-down' )
-            for i in range( int( main.params[ 'CASE70' ][ 'deviceDownWeight' ] ) ):
-                events.append( 'device-down' )
-            for i in range( int( main.params[ 'CASE70' ][ 'portDownWeight' ] ) ):
-                events.append( 'port-down' )
-            if downOnosNum == 0:
-                for i in range( int( main.params[ 'CASE70' ][ 'onosDownWeight' ] ) ):
-                    events.append( 'onos-down' )
-            for i in range( int( pow( hostIntentNum, 1.5 ) / 100 ) ):
-                events.append( 'del-host-intent' )
-            for i in range( int( pow( pointIntentNum, 1.5 ) / 100 ) ):
-                events.append( 'del-point-intent' )
-            for i in range( pow( 4, downLinkNum ) - 1 ):
-                events.append( 'link-up' )
-            for i in range( pow( 4, downDeviceNum ) - 1 ):
-                events.append( 'device-up' )
-            for i in range( pow( 4, downPortNum ) - 1 ):
-                events.append( 'port-up' )
-            for i in range( pow( 4, downOnosNum ) - 1 ):
-                events.append( 'onos-up' )
+            for event, weight in main.params[ 'CASE70' ][ 'eventWeight' ].items():
+                events += [ event ] * int( weight )
+            events += [ 'del-host-intent' ] * int( pow( hostIntentNum, 1.5 ) / 100 )
+            events += [ 'del-point-intent' ] * int( pow( pointIntentNum, 1.5 ) / 100 )
+            events += [ 'device-up' ] * int( pow( 4, downDeviceNum ) - 1 )
+            if 'link-down' in main.params[ 'CASE70' ][ 'eventWeight' ].keys():
+                events += [ 'link-up' ] * int( pow( 4, downLinkNum ) - 1 )
+            if 'port-down' in main.params[ 'CASE70' ][ 'eventWeight' ].keys():
+                events += [ 'port-up' ] * int( pow( 4, downPortNum ) - 1 )
+            events += [ 'onos-up' ] * int( pow( 4, downOnosNum ) - 1 )
             main.log.debug( events )
             event = random.sample( events, 1 )[ 0 ]
             if event == 'add-host-intent':
@@ -852,64 +873,43 @@
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_HOST_ADD, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex )
-                    hostIntentNum += 1
             elif event == 'del-host-intent':
                 n = random.randint( 5, hostIntentNum )
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_HOST_DEL, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex )
-                    hostIntentNum -= 1
             elif event == 'add-point-intent':
                 n = random.randint( 5, 50 )
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_ADD, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex, 'bidirectional' )
-                    pointIntentNum += 2
             elif event == 'del-point-intent':
                 n = random.randint( 5, pointIntentNum / 2 )
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_DEL, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex, 'bidirectional' )
-                    pointIntentNum -= 2
             elif event == 'link-down':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_LINK_DOWN, EventScheduleMethod().RUN_BLOCK, 'random', 'random' )
-                downLinkNum += 1
             elif event == 'link-up':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_LINK_UP, EventScheduleMethod().RUN_BLOCK, 'random', 'random' )
-                downLinkNum -= 1
             elif event == 'device-down':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_DEVICE_DOWN, EventScheduleMethod().RUN_BLOCK, 'random' )
-                downDeviceNum += 1
             elif event == 'device-up':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_DEVICE_UP, EventScheduleMethod().RUN_BLOCK, 'random' )
-                downDeviceNum -= 1
             elif event == 'port-down':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_PORT_DOWN, EventScheduleMethod().RUN_BLOCK, 'random', 'random' )
-                downPortNum += 1
             elif event == 'port-up':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_PORT_UP, EventScheduleMethod().RUN_BLOCK, 'random', 'random' )
-                downPortNum -= 1
-            elif event == 'onos-down':
+            elif event == 'onos-down' and downOnosNum == 0:
                 main.eventGenerator.triggerEvent( EventType().ONOS_ONOS_DOWN, EventScheduleMethod().RUN_BLOCK, 1 )
-                downOnosNum += 1
             elif event == 'onos-up':
                 main.eventGenerator.triggerEvent( EventType().ONOS_ONOS_UP, EventScheduleMethod().RUN_BLOCK, 1 )
-                downOnosNum -= 1
                 main.eventGenerator.triggerEvent( EventType().ONOS_BALANCE_MASTERS, EventScheduleMethod().RUN_BLOCK )
             elif event == 'toggle-flowobj':
-                if not flowObj:
-                    main.eventGenerator.triggerEvent( EventType().ONOS_SET_FLOWOBJ, EventScheduleMethod().RUN_BLOCK, 'true' )
-                else:
-                    main.eventGenerator.triggerEvent( EventType().ONOS_SET_FLOWOBJ, EventScheduleMethod().RUN_BLOCK, 'false' )
-                flowObj = not flowObj
+                main.eventGenerator.triggerEvent( EventType().ONOS_SET_FLOWOBJ, EventScheduleMethod().RUN_BLOCK, 'false' if main.flowObj else 'true' )
             else:
                 pass
-            main.eventGenerator.triggerEvent( EventType().CHECK_TOPO, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventGenerator.triggerEvent( EventType().CHECK_ONOS, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventGenerator.triggerEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventGenerator.triggerEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventGenerator.triggerEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventGenerator.triggerEvent( EventType().CHECK_RAFT_LOG_SIZE, EventScheduleMethod().RUN_NON_BLOCK )
+            main.eventGenerator.triggerEvent( EventType().CHECK_ALL, EventScheduleMethod().RUN_NON_BLOCK )
             with main.eventScheduler.idleCondition:
                 while not main.eventScheduler.isIdle():
                     main.eventScheduler.idleCondition.wait()
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo.flex b/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo.flex
new file mode 100644
index 0000000..e3ad276
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.topo.flex
@@ -0,0 +1,455 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username>ubuntu</karaf_username>
+                <karaf_password>ubuntu</karaf_password>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 3 </nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <OFDPASwitchLeaf201>
+            <host>10.192.21.22</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <shortName>s201</shortName>
+                <dpid>0x201</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>29</port2>
+                    <port1>49</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>30</port2>
+                    <port1>50</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>29</port2>
+                    <port1>51</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>30</port2>
+                    <port1>52</port1>
+                </link4>
+                <link5>
+                    <node2>OFDPASwitchLeaf202</node2>
+                    <port2>20</port2>
+                    <port1>20</port1>
+                </link5>
+            </COMPONENTS>
+        </OFDPASwitchLeaf201>
+
+        <OFDPASwitchLeaf202>
+            <host>10.192.21.23</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+                <shortName>s202</shortName>
+                <dpid>0x202</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>26</port2>
+                    <port1>49</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>28</port2>
+                    <port1>50</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>27</port2>
+                    <port1>51</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf201</node2>
+                    <port2>20</port2>
+                    <port1>20</port1>
+                </link4>
+            </COMPONENTS>
+        </OFDPASwitchLeaf202>
+
+        <OFDPASwitchLeaf203>
+            <host>10.192.21.24</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS>
+                <shortName>s203</shortName>
+                <dpid>0x203</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>23</port2>
+                    <port1>49</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>25</port2>
+                    <port1>50</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>26</port2>
+                    <port1>51</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>24</port2>
+                    <port1>52</port1>
+                </link4>
+                <link5>
+                    <node2>OFDPASwitchLeaf204</node2>
+                    <port2>20</port2>
+                    <port1>20</port1>
+                </link5>
+            </COMPONENTS>
+        </OFDPASwitchLeaf203>
+
+        <OFDPASwitchLeaf204>
+            <host>10.192.21.25</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS>
+                <shortName>s204</shortName>
+                <dpid>0x204</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>32</port2>
+                    <port1>50</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>31</port2>
+                    <port1>51</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>32</port2>
+                    <port1>52</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf203</node2>
+                    <port2>20</port2>
+                    <port1>20</port1>
+                </link4>
+            </COMPONENTS>
+        </OFDPASwitchLeaf204>
+
+        <OFDPASwitchLeaf205>
+            <host>10.192.21.29</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+                <shortName>s205</shortName>
+                <dpid>0x205</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf227</node2>
+                    <port2>25</port2>
+                    <port1>49</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf228</node2>
+                    <port2>25</port2>
+                    <port1>51</port1>
+                </link2>
+            </COMPONENTS>
+        </OFDPASwitchLeaf205>
+
+        <OFDPASwitchLeaf206>
+            <host>10.192.21.30</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>7</connect_order>
+            <COMPONENTS>
+                <shortName>s206</shortName>
+                <dpid>0x206</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf227</node2>
+                    <port2>27</port2>
+                    <port1>49</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf228</node2>
+                    <port2>27</port2>
+                    <port1>51</port1>
+                </link2>
+            </COMPONENTS>
+        </OFDPASwitchLeaf206>
+
+        <OFDPASwitchLeaf225>
+            <host>10.192.21.21</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>8</connect_order>
+            <COMPONENTS>
+                <shortName>s225</shortName>
+                <dpid>0x225</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf227</node2>
+                    <port2>16</port2>
+                    <port1>16</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf203</node2>
+                    <port2>49</port2>
+                    <port1>23</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf203</node2>
+                    <port2>52</port2>
+                    <port1>24</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf202</node2>
+                    <port2>49</port2>
+                    <port1>26</port1>
+                </link4>
+                <link5>
+                    <node2>OFDPASwitchLeaf201</node2>
+                    <port2>51</port2>
+                    <port1>29</port1>
+                </link5>
+                <link6>
+                    <node2>OFDPASwitchLeaf201</node2>
+                    <port2>52</port2>
+                    <port1>30</port1>
+                </link6>
+                <link7>
+                    <node2>OFDPASwitchLeaf204</node2>
+                    <port2>51</port2>
+                    <port1>31</port1>
+                </link7>
+                <link8>
+                    <node2>OFDPASwitchLeaf204</node2>
+                    <port2>52</port2>
+                    <port1>32</port1>
+                </link8>
+            </COMPONENTS>
+        </OFDPASwitchLeaf225>
+
+        <OFDPASwitchLeaf226>
+            <host>10.192.21.26</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>9</connect_order>
+            <COMPONENTS>
+                <shortName>s226</shortName>
+                <dpid>0x226</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf228</node2>
+                    <port2>16</port2>
+                    <port1>16</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf203</node2>
+                    <port2>50</port2>
+                    <port1>25</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf203</node2>
+                    <port2>51</port2>
+                    <port1>26</port1>
+                </link3>
+                <link4>
+                    <node2>OFDPASwitchLeaf202</node2>
+                    <port2>51</port2>
+                    <port1>27</port1>
+                </link4>
+                <link5>
+                    <node2>OFDPASwitchLeaf202</node2>
+                    <port2>50</port2>
+                    <port1>28</port1>
+                </link5>
+                <link6>
+                    <node2>OFDPASwitchLeaf201</node2>
+                    <port2>49</port2>
+                    <port1>29</port1>
+                </link6>
+                <link7>
+                    <node2>OFDPASwitchLeaf201</node2>
+                    <port2>50</port2>
+                    <port1>30</port1>
+                </link7>
+                <link8>
+                    <node2>OFDPASwitchLeaf204</node2>
+                    <port2>50</port2>
+                    <port1>32</port1>
+                </link8>
+            </COMPONENTS>
+        </OFDPASwitchLeaf226>
+
+        <OFDPASwitchLeaf227>
+            <host>10.192.21.28</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>10</connect_order>
+            <COMPONENTS>
+                <shortName>s227</shortName>
+                <dpid>0x227</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf225</node2>
+                    <port2>16</port2>
+                    <port1>16</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf205</node2>
+                    <port2>49</port2>
+                    <port1>25</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf206</node2>
+                    <port2>49</port2>
+                    <port1>27</port1>
+                </link3>
+            </COMPONENTS>
+        </OFDPASwitchLeaf227>
+
+        <OFDPASwitchLeaf228>
+            <host>10.192.21.31</host>
+            <user>root</user>
+            <password></password>
+            <type>OFDPASwitchDriver</type>
+            <connect_order>11</connect_order>
+            <COMPONENTS>
+                <shortName>s228</shortName>
+                <dpid>0x228</dpid>
+                <confDir>/etc/ofdpa/</confDir>
+                <link1>
+                    <node2>OFDPASwitchLeaf226</node2>
+                    <port2>16</port2>
+                    <port1>16</port1>
+                </link1>
+                <link2>
+                    <node2>OFDPASwitchLeaf205</node2>
+                    <port2>51</port2>
+                    <port1>25</port1>
+                </link2>
+                <link3>
+                    <node2>OFDPASwitchLeaf206</node2>
+                    <port2>51</port2>
+                    <port1>27</port1>
+                </link3>
+            </COMPONENTS>
+        </OFDPASwitchLeaf228>
+
+        <Host4v4>
+            <host>10.192.21.61</host>
+            <user>vyatta</user>
+            <password>vyatta</password>
+            <type>HostDriver</type>
+            <connect_order>13</connect_order>
+            <COMPONENTS>
+                <ip>10.0.202.7</ip>
+                <ip6></ip6>
+                <shortName>h4v4</shortName>
+                <mac>0A:5E:0D:F3:EC:D4</mac>
+                <ifaceName>bond0</ifaceName>
+                <inband>True</inband>
+                <username>ubuntu</username>
+                <password>ubuntu</password>
+            </COMPONENTS>
+        </Host4v4>
+
+        <Host5v4>
+            <host>10.192.21.61</host>
+            <user>vyatta</user>
+            <password>vyatta</password>
+            <type>HostDriver</type>
+            <connect_order>14</connect_order>
+            <COMPONENTS>
+                <ip>10.0.202.8</ip>
+                <ip6></ip6>
+                <shortName>h5v4</shortName>
+                <mac>12:1C:B7:5C:69:68</mac>
+                <ifaceName>bond0</ifaceName>
+                <inband>True</inband>
+                <username>ubuntu</username>
+                <password>ubuntu</password>
+            </COMPONENTS>
+        </Host5v4>
+
+        <Host9v4>
+            <host>10.192.21.61</host>
+            <user>vyatta</user>
+            <password>vyatta</password>
+            <type>HostDriver</type>
+            <connect_order>15</connect_order>
+            <COMPONENTS>
+                <ip>10.0.204.7</ip>
+                <ip6></ip6>
+                <shortName>h9v4</shortName>
+                <mac>EA:DA:19:1E:CB:7D</mac>
+                <ifaceName>bond0</ifaceName>
+                <inband>True</inband>
+                <username>ubuntu</username>
+                <password>ubuntu</password>
+            </COMPONENTS>
+        </Host9v4>
+
+        <Host10v4>
+            <host>10.192.21.61</host>
+            <user>vyatta</user>
+            <password>vyatta</password>
+            <type>HostDriver</type>
+            <connect_order>16</connect_order>
+            <COMPONENTS>
+                <ip>10.0.204.8</ip>
+                <ip6></ip6>
+                <shortName>h10v4</shortName>
+                <mac>A2:9B:16:E8:2A:52</mac>
+                <ifaceName>bond0</ifaceName>
+                <inband>True</inband>
+                <username>ubuntu</username>
+                <password>ubuntu</password>
+            </COMPONENTS>
+        </Host10v4>
+
+        <NetworkBench>
+            <host>localhost</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>NetworkDriver</type>
+            <connect_order>20</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </NetworkBench>
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
index 8d347c1..35bad28 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
@@ -314,7 +314,6 @@
     def startPointIntentEvent( self ):
         try:
             assert self.deviceA is not None and self.deviceB is not None
-            controller = main.controllers[ self.CLIIndex - 1 ]
             # TODO: support multiple hosts under one device
             # Check whether there already exists some intent for the device pair
             # For now we should avoid installing overlapping intents
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
index 22380cf..9614696 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -109,8 +109,8 @@
                     switchList = []
                     for device in main.devices:
                         switchList.append( device.name )
-                    with main.mininetLock:
-                        flowCompareResult = main.Mininet1.checkFlowId( switchList, flowIDList, debug=False )
+                    with main.networkLock:
+                        flowCompareResult = main.Network.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
@@ -149,16 +149,16 @@
         # Verify host IPs
         if hasattr( main, "expectedHosts" ):
             for host in upHostList:
-                ipResult = main.Mininet1.verifyHostIp( hostList=[ host.name ],
+                ipResult = main.Network.verifyHostIp( hostList=[ host.name ],
                                                        prefix=main.expectedHosts[ 'network' ][ host.name ],
                                                        update=False )
                 if not ipResult:
                     checkResult = EventStates().FAIL
                     main.log.warn( "Topo Check - Failed to verify IP address on host %s" % ( host.name ) )
         '''
-        with main.mininetLock:
-            graphDictMininet = main.Mininet1.getGraphDict( useId=True, switchClasses=r"(OVSSwitch)",
-                                                           excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ] )
+        with main.networkLock:
+            graphDictMininet = main.Network.getGraphDict( useId=True,
+                                                          excludeNodes=main.excludeNodes )
         '''
         for controller in main.controllers:
             if controller.isUp():
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
index 7a2e84f..90f74a9 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -50,9 +50,9 @@
                 return EventStates().ABORT
             if args[ 0 ] == 'random' or args[ 1 ] == 'random':
                 if self.typeIndex == EventType().NETWORK_LINK_DOWN:
-                    with main.mininetLock:
-                        linkRandom = main.Mininet1.getLinkRandom( switchClasses=r"(OVSSwitch)",
-                                                                  excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ] )
+                    with main.networkLock:
+                        linkRandom = main.Network.getLinkRandom( excludeNodes=main.excludeNodes,
+                                                                 skipLinks=main.skipLinks )
                     if linkRandom is None:
                         main.log.warn( "No link available, aborting event" )
                         return EventStates().ABORT
@@ -110,14 +110,14 @@
                 main.log.warn( "Link Down - link has been removed" )
                 return EventStates().ABORT
         main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.linkA.deviceA.name, self.linkA.deviceB.name ) )
-        with main.mininetLock:
+        with main.networkLock:
             """
-            result = main.Mininet1.link( END1=self.linkA.deviceA.name,
-                                         END2=self.linkA.deviceB.name,
-                                         OPTION="down" )
+            result = main.Network.link( END1=self.linkA.deviceA.name,
+                                        END2=self.linkA.deviceB.name,
+                                        OPTION="down" )
             """
-            result = main.Mininet1.delLink( self.linkA.deviceA.name,
-                                            self.linkA.deviceB.name )
+            result = main.Network.delLink( self.linkA.deviceA.name,
+                                           self.linkA.deviceB.name )
         if not result:
             main.log.warn( "%s - failed to bring down link" % ( self.typeString ) )
             return EventStates().FAIL
@@ -147,14 +147,14 @@
                 main.log.warn( "Link Up - link has been removed" )
                 return EventStates().ABORT
         main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.linkA.deviceA.name, self.linkA.deviceB.name ) )
-        with main.mininetLock:
+        with main.networkLock:
             """
-            result = main.Mininet1.link( END1=self.linkA.deviceA.name,
-                                         END2=self.linkA.deviceB.name,
-                                         OPTION="up" )
+            result = main.Network.link( END1=self.linkA.deviceA.name,
+                                        END2=self.linkA.deviceB.name,
+                                        OPTION="up" )
             """
-            result = main.Mininet1.addLink( self.linkA.deviceA.name,
-                                            self.linkA.deviceB.name )
+            result = main.Network.addLink( self.linkA.deviceA.name,
+                                           self.linkA.deviceB.name )
         if not result:
             main.log.warn( "%s - failed to bring up link" % ( self.typeString ) )
             return EventStates().FAIL
@@ -188,14 +188,9 @@
             if args[ 0 ] == 'random':
                 import random
                 if self.typeIndex == EventType().NETWORK_DEVICE_DOWN:
-                    if main.params[ 'TOPO' ][ 'excludeSwitches' ]:
-                        excludeSwitches = main.params[ 'TOPO' ][ 'excludeSwitches' ].split( ',' )
-                    else:
-                        excludeSwitches = []
-                    with main.mininetLock:
-                        switchRandom = main.Mininet1.getSwitchRandom( switchClasses=r"(OVSSwitch)",
-                                                                      excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ],
-                                                                      excludeSwitches=excludeSwitches )
+                    with main.networkLock:
+                        switchRandom = main.Network.getSwitchRandom( excludeNodes=main.excludeNodes,
+                                                                     skipSwitches=main.skipSwitches )
                     if switchRandom is None:
                         main.log.warn( "No switch available, aborting event" )
                         return EventStates().ABORT
@@ -240,14 +235,14 @@
                 return EventStates().ABORT
         main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.device.name ) )
         result = main.TRUE
-        with main.mininetLock:
+        with main.networkLock:
             # Disable ports toward dual-homed hosts
             for host, port in self.device.hosts.items():
                 if host.isDualHomed:
                     main.log.info( "Disable port {}/{} which connects to a dual-homed host before bringing down this device".format( self.device.dpid, port ) )
                     result = result and main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=port, state="disable" )
-            # result = main.Mininet1.delSwitch( self.device.name )
-            result = result and main.Mininet1.switch( SW=self.device.name, OPTION="stop" )
+            # result = main.Network.delSwitch( self.device.name )
+            result = result and main.Network.switch( SW=self.device.name, OPTION="stop" )
         if not result:
             main.log.warn( "%s - failed to bring down device" % ( self.typeString ) )
             return EventStates().FAIL
@@ -282,53 +277,29 @@
                 return EventStates().ABORT
         # Re-add the device
         main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.device.name ) )
-        with main.mininetLock:
-            # result = main.Mininet1.addSwitch( self.device.name, dpid=self.device.dpid[ 3: ] )
-            result = main.Mininet1.switch( SW=self.device.name, OPTION='start' )
-        if not result:
-            main.log.warn( "%s - failed to re-add device" % ( self.typeString ) )
-            return EventStates().FAIL
+        if hasattr( main, 'Mininet1' ):
+            with main.networkLock:
+                # result = main.Network.addSwitch( self.device.name, dpid=self.device.dpid[ 3: ] )
+                result = main.Network.switch( SW=self.device.name, OPTION='start' )
+            if not result:
+                main.log.warn( "%s - failed to re-add device" % ( self.typeString ) )
+                return EventStates().FAIL
+        # Re-assign mastership for the device
+        with main.networkLock:
+            result = main.Network.assignSwController( sw=self.device.name, ip=main.Cluster.getIps() )
+            if not result:
+                main.log.warn( "%s - failed to assign device to controller" % ( 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:
-            ips = main.Cluster.getIps()
-            main.Mininet1.assignSwController( sw=self.device.name, ip=ips )
-        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
+            # FIXME: remove this temporary hack for CORD-3240
+            if neighbor.name == 's225':
+                main.NetworkBench.switches[ 's225' ].setPortSpeed( index=link.portB )
             # Bring down again any link that was brought down before the device was down
             if int( link.portB ) in link.deviceB.downPorts:
                 with main.variableLock:
@@ -340,7 +311,7 @@
                     link.backwardLink.bringUp()
         # Re-discover hosts
         if self.device.hosts:
-            main.Mininet1.discoverHosts( hostList=[ host.name for host in self.device.hosts ] )
+            main.Network.discoverHosts( hostList=[ host.name for host in self.device.hosts ] )
         for host in self.device.hosts:
             with main.variableLock:
                 host.bringUp()
@@ -373,9 +344,9 @@
                 return EventStates().ABORT
             if args[ 0 ] == 'random' or args[ 1 ] == 'random':
                 if self.typeIndex == EventType().NETWORK_PORT_DOWN:
-                    with main.mininetLock:
-                        linkRandom = main.Mininet1.getLinkRandom( switchClasses=r"(OVSSwitch)",
-                                                                  excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ])
+                    with main.networkLock:
+                        linkRandom = main.Network.getLinkRandom( excludeNodes=main.excludeNodes,
+                                                                 skipLinks=main.skipLinks )
                     if linkRandom is None:
                         main.log.warn( "No link available, aborting event" )
                         return EventStates().ABORT
@@ -443,7 +414,7 @@
             main.log.warn( "port Down - link has been removed" )
             return EventStates().ABORT
         main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.device.name, self.port ) )
-        with main.mininetLock:
+        with main.networkLock:
             result = main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=self.port, state="disable" )
         if not result:
             main.log.warn( "%s - failed to bring down port" % ( self.typeString ) )
@@ -474,11 +445,14 @@
             main.log.warn( "port up - link has been removed" )
             return EventStates().ABORT
         main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.device.name, self.port ) )
-        with main.mininetLock:
+        with main.networkLock:
             result = main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=self.port, state="enable" )
         if not result:
             main.log.warn( "%s - failed to bring up port " % ( self.typeString ) )
             return EventStates().FAIL
+        # FIXME: remove this temporary hack for CORD-3240
+        if self.link.deviceB.name == 's225':
+            main.NetworkBench.switches[ 's225' ].setPortSpeed( index=self.link.portB )
         with main.variableLock:
             self.device.downPorts.remove( self.port )
             self.link.bringUp()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
index 0fa7f25..00c58c5 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
@@ -202,6 +202,7 @@
         if not result:
             main.log.warn( "%s - failed to set configuration" % ( self.typeString ) )
             return EventStates().FAIL
+        main.flowObj = True if self.value == 'true' else False
         return EventStates().PASS
 
 
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/host/flex.host b/TestON/tests/CHOTestMonkey/dependencies/topologies/host/flex.host
new file mode 100644
index 0000000..0ccf382
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/host/flex.host
@@ -0,0 +1,16 @@
+{
+    "onos":
+    {
+        "0A:5E:0D:F3:EC:D4/None": "10.0.202.7",
+        "12:1C:B7:5C:69:68/None": "10.0.202.8",
+        "EA:DA:19:1E:CB:7D/None": "10.0.204.7",
+        "A2:9B:16:E8:2A:52/None": "10.0.204.8"
+    },
+    "network":
+    {
+        "h4v4": "10.0.202.7",
+        "h5v4": "10.0.202.8",
+        "h9v4": "10.0.204.7",
+        "h10v4": "10.0.204.8"
+    }
+}
diff --git a/TestON/tests/CHOTestMonkey/dependencies/topologies/json/flex.json b/TestON/tests/CHOTestMonkey/dependencies/topologies/json/flex.json
new file mode 100644
index 0000000..c8f1ff2
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/topologies/json/flex.json
@@ -0,0 +1,460 @@
+{
+    "ports" : {
+        "of:0000000000000205/1" : {
+    	    "interfaces" : [{
+    		    "ips" : [ "10.0.205.254/24" ],
+    		    "vlan-untagged": 205
+    	    }]
+    	},
+        "of:0000000000000205/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.205.254/24" ],
+                "vlan-untagged": 205
+            }]
+        },
+        "of:0000000000000206/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.206.254/24" ],
+                "vlan-untagged": 206
+            }]
+        },
+        "of:0000000000000206/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.206.254/24" ],
+                "vlan-untagged": 206
+            }]
+        },
+        "of:0000000000000203/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/3" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/4" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/5" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/6" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/7" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/8" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000203/20" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-tagged": [204]
+            }]
+        },
+        "of:0000000000000204/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/3" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/4" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/5" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/6" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/7" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/8" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-untagged": 204
+            }]
+        },
+        "of:0000000000000204/20" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.204.254/24" ],
+                "vlan-tagged": [204]
+            }]
+        },
+        "of:0000000000000201/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/3" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/4" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/5" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/6" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/7" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/8" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000201/17" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.101.254/24" ],
+                "vlan-untagged": 101
+            }]
+        },
+        "of:0000000000000201/18" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.101.254/24" ],
+                "vlan-untagged": 101
+            }]
+        },
+        "of:0000000000000201/20" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-tagged": [202]
+            }]
+        },
+        "of:0000000000000202/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/3" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/4" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/5" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/6" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/7" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/8" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-untagged": 202
+            }]
+        },
+        "of:0000000000000202/17" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.102.254/24" ],
+                "vlan-untagged": 102
+            }]
+        },
+        "of:0000000000000202/18" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.102.254/24" ],
+                "vlan-untagged": 102
+            }]
+        },
+        "of:0000000000000202/20" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.202.254/24" ],
+                "vlan-tagged": [202]
+            }]
+        }
+    },
+    "devices" : {
+        "of:0000000000000201" : {
+	        "basic" : {
+	    	    "name": "201-qmx",
+	    	    "latitude" : 34,
+	    	    "longitude": -95
+	        },
+            "segmentrouting" : {
+                "ipv4NodeSid" : 201,
+                "ipv4Loopback" : "192.168.0.201",
+                "ipv6NodeSid" : 201,
+                "ipv6Loopback" : "2000::c0a8:0201",
+                "routerMac" : "00:00:02:01:02:02",
+	    	    "pairDeviceId" : "of:0000000000000202",
+	    	    "pairLocalPort" : 20,
+                "isEdgeRouter" : true,
+                "adjacencySids" : []
+                }
+            },
+	    "of:0000000000000202" : {
+	        "basic" : {
+	    	    "name": "202-qmx",
+	    	    "latitude" : 34,
+	    	    "longitude": -90
+	        },
+	        "segmentrouting" : {
+                "ipv4NodeSid" : 202,
+                "ipv4Loopback" : "192.168.0.202",
+                "ipv6NodeSid" : 202,
+                "ipv6Loopback" : "2000::c0a8:0202",
+                "routerMac" : "00:00:02:01:02:02",
+                "isEdgeRouter" : true,
+                "pairDeviceId" : "of:0000000000000201",
+                "pairLocalPort" : 20,
+	    	    "adjacencySids" : []
+            }
+        },
+        "of:0000000000000203" : {
+	        "basic" : {
+	            "name": "203-qmx",
+	            "latitude" : 34,
+	            "longitude": -108
+	        },
+            "segmentrouting" : {
+                "name" : "Leaf2-R1",
+                "ipv4NodeSid" : 203,
+                "ipv4Loopback" : "192.168.0.203",
+                "ipv6NodeSid" : 203,
+                "ipv6Loopback" : "2000::c0a8:0203",
+                "routerMac" : "00:00:02:03:02:04",
+                "isEdgeRouter" : true,
+                "pairDeviceId" : "of:0000000000000204",
+                "pairLocalPort" : 20,
+                "adjacencySids" : []
+            }
+        },
+        "of:0000000000000204" : {
+	        "basic" : {
+	            "name": "204-qmx",
+	            "latitude" : 34,
+	            "longitude": -103
+	        },
+            "segmentrouting" : {
+                "name" : "Leaf1-R1",
+                "ipv4NodeSid" : 204,
+                "ipv4Loopback" : "192.168.0.204",
+                "ipv6NodeSid" : 204,
+                "ipv6Loopback" : "2000::c0a8:0204",
+                "routerMac" : "00:00:02:03:02:04",
+                "pairDeviceId" : "of:0000000000000203",
+                "pairLocalPort" : 20,
+                "isEdgeRouter" : true,
+                "adjacencySids" : []
+            }
+        },
+   	    "of:0000000000000225" : {
+	        "basic" : {
+	    	    "name": "225-t2",
+	    	    "latitude" : 42,
+	    	    "longitude": -100
+	        },
+        	"segmentrouting" : {
+                "name" : "SpineToma-0",
+                "ipv4NodeSid" : 225,
+                "ipv4Loopback" : "192.168.0.225",
+	    	    "ipv6NodeSid" : 225,
+                "ipv6Loopback" : "2000::c0a8:0225",
+                "routerMac" : "00:00:02:25:00:01",
+                "isEdgeRouter" : false,
+	    	    "adjacencySids" : []
+            }
+        },
+        "of:0000000000000226" : {
+	        "basic" : {
+	    	    "name": "226-tmhk",
+	    	    "latitude" : 42,
+	    	    "longitude": -95
+	        },
+            "segmentrouting" : {
+                "name" : "Spine1",
+                "ipv4NodeSid" : 226,
+                "ipv4Loopback" : "192.168.0.226",
+                "ipv6NodeSid" : 226,
+                "ipv6Loopback" : "2000::c0a8:0226",
+                "routerMac" : "00:00:02:26:00:01",
+                "isEdgeRouter" : false,
+                "adjacencySids" : []
+            }
+        },
+        "of:0000000000000205":{
+            "basic":{
+                "name":"205-qmx",
+                "latitude":34,
+                "longitude":-120
+            },
+            "segmentrouting":{
+                "name":"Leaf1",
+                "ipv4NodeSid":205,
+                "ipv4Loopback":"192.168.0.205",
+                "ipv6NodeSid":205,
+                "ipv6Loopback":"2000::c0a8:0205",
+                "routerMac":"00:00:02:05:06:01",
+                "isEdgeRouter":true,
+                "adjacencySids":[
+                ]
+            }
+        },
+        "of:0000000000000206":{
+            "basic":{
+                "name":"206-qmx",
+                "latitude":34,
+                "longitude":-115
+            },
+            "segmentrouting":{
+                "name":"Leaf2",
+                "ipv4NodeSid":206,
+                "ipv4Loopback":"192.168.0.206",
+                "ipv6NodeSid":206,
+                "ipv6Loopback":"2000::c0a8:0206",
+                "routerMac":"00:00:02:06:06:01",
+                "isEdgeRouter":true,
+                "adjacencySids":[
+                ]
+            }
+        },
+        "of:0000000000000227":{
+            "basic":{
+                "name":"227-tmhk",
+                "latitude":38,
+                "longitude":-119
+            },
+            "segmentrouting":{
+                "name":"Spine1",
+                "ipv4NodeSid":227,
+                "ipv4Loopback":"192.168.0.227",
+                "ipv6NodeSid":227,
+                "ipv6Loopback":"2000::c0a8:0227",
+                "routerMac":"00:00:02:27:00:01",
+                "isEdgeRouter":false,
+                "adjacencySids":[
+                ]
+            }
+        },
+        "of:0000000000000228":{
+            "basic":{
+                "name":"228-t2",
+                "latitude":38,
+                "longitude":-116
+            },
+            "segmentrouting":{
+                "name":"Spine2",
+                "ipv4NodeSid":228,
+                "ipv4Loopback":"192.168.0.228",
+                "ipv6NodeSid":228,
+                "ipv6Loopback":"2000::c0a8:0228",
+                "routerMac":"00:00:02:28:00:01",
+                "isEdgeRouter":false,
+                "adjacencySids":[
+                ]
+            }
+        }
+    },
+    "apps" : {
+            "org.onosproject.dhcprelay" : {
+    	    "default" : [
+    		    {
+                    "dhcpServerConnectPoint": "of:0000000000000204/8",
+                    "serverIps": ["10.0.204.8"]
+    		    }
+    	    ]
+        }
+    }
+}
diff --git a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
index dc9cb52..4342bfe 100644
--- a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
+++ b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
@@ -277,12 +277,14 @@
             main.log.info( resultString )
             print( "\n" )
 
-        avgOfMaxes = numpy.mean( maxes )
-        main.log.info( "Average of max value from each test iteration: " + str( avgOfMaxes ) )
-
-        stdOfMaxes = numpy.std( maxes )
-        main.log.info( "Standard Deviation of max values: " + str( stdOfMaxes ) )
-        print( "\n\n" )
+        try:
+            avgOfMaxes = numpy.mean( maxes )
+            main.log.info( "Average of max value from each test iteration: " + str( avgOfMaxes ) )
+            stdOfMaxes = numpy.std( maxes )
+            main.log.info( "Standard Deviation of max values: " + str( stdOfMaxes ) )
+            print( "\n\n" )
+        except TypeError:
+            main.cleanAndExit( msg="flow-tester execution failed" )
 
         avgTP = int( installCount ) / avgOfMaxes  # result in kflows/second
 
diff --git a/TestON/tests/dependencies/ONOSSetup.py b/TestON/tests/dependencies/ONOSSetup.py
index d7ea792..8371e0b 100644
--- a/TestON/tests/dependencies/ONOSSetup.py
+++ b/TestON/tests/dependencies/ONOSSetup.py
@@ -310,6 +310,8 @@
                                  actual=packageResult,
                                  onpass="Successfully created ONOS package",
                                  onfail="Failed to create ONOS package" )
+        if not packageResult:
+            main.cleanAndExit()
         return packageResult
 
     def installAtomix( self, cluster, parallel=True ):
@@ -477,7 +479,7 @@
             else:
                 functions( *args ) if args is not None else functions()
 
-    def ONOSSetUp( self, cluster, hasMultiNodeRounds=False, startOnos=True, newCell=True,
+    def ONOSSetUp( self, cluster, hasMultiNodeRounds=False, startOnosCli=True, newCell=True,
                    cellName="temp", cellApps="drivers", appPrefix="org.onosproject.",
                    mininetIp="", extraApply=None, applyArgs=None,
                    extraClean=None, cleanArgs=None, skipPack=False, installMax=False,
@@ -503,7 +505,7 @@
             * cluster - the cluster driver that will be used.
         Optional Arguments:
             * hasMultiNodeRounds - True if the test is testing different set of nodes
-            * startOnos - True if wish to start onos.
+            * startOnosCli - True if wish to start onos CLI.
             * newCell - True for making a new cell and False for not making it.
             * cellName - Name of the cell that will be used.
             * cellApps - The cell apps string. Will be overwritten by main.apps if it exists
@@ -589,7 +591,7 @@
         onosServiceResult = self.checkOnosService( cluster )
 
         onosCliResult = main.TRUE
-        if startOnos:
+        if startOnosCli:
             onosCliResult = self.startOnosClis( cluster )
 
         onosNodesResult = self.checkOnosNodes( cluster )