Merge "Change Mininet IP from localhost to OCN"
diff --git a/.gitignore b/.gitignore
index fa270ed..b70404d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@
 *.pyc
 .DS_Store
 ._.DS_Store
-
+.idea/*
diff --git a/README.md b/README.md
index ad22ef6..49ded69 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-TestON, a testing infastructure by Paxterra
+TestON, a testing infastructure by Paxterra and Open Networking Labs
 =======================================
-TestON is the testing platform that all the ONOS tests are being run on currently. 
+TestON is the testing platform that all the ONOS tests are being run on currently.
 
 
 Code Style
@@ -11,27 +11,27 @@
 Setup
 -------------
 
-0. Pull the git repo from https://github.com/OPENNETWORKINGLAB/OnosSystemTest.git 
+0. Pull the git repo from gerrit or github(Read only mirror)
+
+    $ git clone https://gerrit.onosproject.org/OnosSystemTest
+
+    or
 
     $ git clone https://github.com/OPENNETWORKINGLAB/OnosSystemTest.git
 
-1. Make a symbolic link for TestON on the HOMEDIR 
-   Execute the following from the home directory  
+1. Run the install script
 
-    $ ln -s OnosSystemTest/TestON TestON
+    $ cd OnosSystemTest/TestON
 
-2. Make sure python path is correct 
+    $ install.sh
 
-    $ export PYTHONPATH={PATH TO HOMEDIR}/TestON/
-
-    $ echo $PYTHONPATH 
 
 
 Dependencies
 ------------
-1. ONOS
+1. [ONOS](https://github.com/opennetworkinglab/onos) - The system under test
 
-2. Mininet - Some driver functions rely on a modified version of Mininet. These functions are noted in the mininet driver file. To checkout this branch from your Mininet folder:
+2. [Mininet](https://github.com/mininet/mininet) - A Network Emulator. NOTE: Some driver functions rely on a modified version of Mininet. These functions are noted in the mininet driver file. To checkout this branch from your Mininet folder:
 
     $ git remote add jhall11 https://github.com/jhall11/mininet.git
 
@@ -43,13 +43,7 @@
 
     Note that you may need to run 'sudo make develop' if your mnexec.c file changed when switching branches.
 
-3. Install python packages configObj and pexpect (Note: pexpect 3.3 has a known bug. We recommend using version 3.2 for now). They can be installed as :
-
-    $ sudo pip install configObj
-
-    $ sudo pip install pexpect==3.2
-
-    $ sudo pip install numpy
+3. There are some python modules required by some test cases. These modules should be installed by running the install.sh script.
 
 4. Linc-OE - Some testcases use this to emulate optical devices
 
@@ -83,53 +77,51 @@
     Change the file paths to the appropriate paths
 
 2. The .topo file for each test
- 
+
     Must change the IPs/login/etc to point to the nodes you want to run on
 
 Running TestON
 ------------
 
-1. TestON must be ran from its bin directory 
+1. TestON must be ran from its bin directory
 
     $ cd TestON/bin
 
-2. Launch cli
+2. Run the test
 
-    $ ./cli.py 
-
-3. Run the test 
-
-    teston> run PingallExample
+    $ teston run SAMPstartTemplate_1node
 
 The Tests
 -----------------------------------------------
 
 The tests are all located it TestON/tests/
-Each test has its own folder with the following files: 
+Each test has its own folder with the following files:
 
-1. .ospk file
+1. .ospk file (optional)
 
     - This is written in Openspeak, a word based language developed by Paxterra.
 
-    - It defines the cases and sequence of events for the test 
+    - It defines the cases and sequence of events for the test
+
+    - TestON will automatically generate the .py file based on the .ospk file if the .ospk file exists.
 
 2. .py file
- 
-    - This file serves the same exact function as the openspeak file. 
 
-    - It will only be used if there is NO .ospk file, so if you like python, delete or rename the .ospk file 
- 
-3. .topo file  
+    - This file serves the same exact function as the openspeak file.
 
-    - This defines all the components that TestON creates for that test and includes data such as IP address, login info, and device drivers  
- 
-    - The Components must be defined in this file to be uesd in the openspeak or python files. 
-    
+    - It will only be used if there is NO .ospk file, so if you like python, delete or rename the .ospk file
+
+3. .topo file
+
+    - This defines all the components that TestON creates for that test and includes data such as IP address, login info, and device drivers
+
+    - The Components must be defined in this file to be uesd in the openspeak or python files.
+
 4. .params file
 
-    - Defines all the test-specific variables that are used by the test. 
+    - Defines all the test-specific variables that are used by the test.
 
-    - NOTE: The variable `testcases` which defines which testcases run when the test is ran. 
+    - NOTE: The variable `testcases` defines which testcases run when the test is ran.
 
 Troubleshooting
 -----------------------------------------------
@@ -137,4 +129,4 @@
 
 1. Double check the topo file for that specific test the nodes must be able to run that specific component ( Mininet IP -> machine with mn installed)
 
-2. Enable passwordless logins between your nodes and the TestON node.  
+2. Enable passwordless logins between your nodes and the TestON node.
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 1209571..1632fee 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -38,6 +38,7 @@
 import os
 from math import pow
 from drivers.common.cli.emulatordriver import Emulator
+from core.graph import Graph
 
 
 class MininetCliDriver( Emulator ):
@@ -57,6 +58,7 @@
         self.hostPrompt = "~#"
         self.bashPrompt = "\$"
         self.scapyPrompt = ">>>"
+        self.graph = Graph()
 
     def connect( self, **connectargs ):
         """
@@ -1827,6 +1829,72 @@
             main.cleanup()
             main.exit()
 
+    def getSwitchRandom( self, timeout=60, nonCut=True ):
+        """
+        Randomly get a switch from Mininet 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.
+        Returns the name of the chosen switch.
+        """
+        import random
+        candidateSwitches = []
+        try:
+            if not nonCut:
+                switches = self.getSwitches( timeout=timeout )
+                assert len( switches ) != 0
+                for switchName in switches.keys():
+                    candidateSwitches.append( switchName )
+            else:
+                graphDict = self.getGraphDict( timeout=timeout, useId=False )
+                if graphDict == None:
+                    return None
+                self.graph.update( graphDict )
+                candidateSwitches = self.graph.getNonCutVertices()
+            if candidateSwitches == 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 delSwitchRandom( self, timeout=60, nonCut=True ):
+        """
+        Randomly delete a switch from Mininet 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 chooses one for deletion,
+        otherwise it just randomly delete one switch from all current
+        switches in Mininet.
+        Returns the name of the deleted switch
+        """
+        try:
+            switch = self.getSwitchRandom( timeout, nonCut )
+            if switch == None:
+                return None
+            else:
+                deletionResult = self.delSwitch( switch )
+            if deletionResult:
+                return switch
+            else:
+                return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
     def addLink( self, node1, node2 ):
         """
            add a link to the mininet topology
@@ -1892,6 +1960,75 @@
             main.cleanup()
             main.exit()
 
+    def getLinkRandom( self, timeout=60, nonCut=True ):
+        """
+        Randomly get a link from Mininet 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 in
+        Mininet.
+        Returns the link as a list, e.g. [ 's1', 's2' ]
+        """
+        import random
+        candidateLinks = []
+        try:
+            if not nonCut:
+                links = self.getLinks( timeout=timeout )
+                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 )
+                if graphDict == None:
+                    return None
+                self.graph.update( graphDict )
+                candidateLinks = self.graph.getNonCutEdges()
+            if candidateLinks == 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 delLinkRandom( self, timeout=60, nonCut=True ):
+        """
+        Randomly delete a link from Mininet 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 chooses one for deletion,
+        otherwise it just randomly delete one link from all current links
+        in Mininet.
+        Returns the deleted link as a list, e.g. [ 's1', 's2' ]
+        """
+        try:
+            link = self.getLinkRandom( timeout, nonCut )
+            if link == None:
+                return None
+            else:
+                deletionResult = self.delLink( link[ 0 ], link[ 1 ] )
+            if deletionResult:
+                return link
+            else:
+                return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
     def addHost( self, hostname, **kwargs ):
         """
         Add a host to the mininet topology
@@ -2476,6 +2613,48 @@
                             'enabled': isUp } )
         return ports
 
+    def getOVSPorts( self, nodeName ):
+        """
+        Read ports from OVS by executing 'ovs-ofctl dump-ports-desc' command.
+
+        Returns a list of dictionaries containing information about each
+        port of the given switch.
+        """
+        command = "sh ovs-ofctl dump-ports-desc " + str( nodeName )
+        try:
+            response = self.execute(
+                cmd=command,
+                prompt="mininet>",
+                timeout=10 )
+            ports = []
+            if response:
+                for line in response.split( "\n" ):
+                    # Regex patterns to parse 'ovs-ofctl dump-ports-desc' output
+                    # Example port:
+                    # 1(s1-eth1): addr:ae:60:72:77:55:51
+                    pattern = "(?P<index>\d+)\((?P<name>[^-]+-eth(?P<port>\d+))\):\saddr:(?P<mac>([a-f0-9]{2}:){5}[a-f0-9]{2})"
+                    result = re.search( pattern, line )
+                    if result:
+                        index = result.group( 'index' )
+                        name = result.group( 'name' )
+                        # This port number is extracted from port name
+                        port = result.group( 'port' )
+                        mac = result.group( 'mac' )
+                        ports.append( { 'index': index,
+                                        'name': name,
+                                        'port': port,
+                                        'mac': mac } )
+            return ports
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
     def getSwitches( self, verbose=False ):
         """
         Read switches from Mininet.
@@ -2963,6 +3142,100 @@
 
         return switchList
 
+    def getGraphDict( self, timeout=60, useId=True, includeHost=False ):
+        """
+        Return a dictionary which describes the latest Mininet 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 Mininet topology with ONOS
+        topology.
+        If includeHost == True, all hosts (and host-switch links) will be included
+        in topology data.
+        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
+        recommended to use delLink() or delSwitch functions to simulate link/switch
+        down, and addLink() or addSwitch to add them back.
+        """
+        graphDict = {}
+        try:
+            links = self.getLinks( timeout=timeout )
+            portDict = {}
+            if useId:
+                switches = self.getSwitches()
+            if includeHost:
+                hosts = self.getHosts()
+            for link in links:
+                # FIXME: support 'includeHost' argument
+                if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
+                    continue
+                nodeName1 = link[ 'node1' ]
+                nodeName2 = link[ 'node2' ]
+                port1 = link[ 'port1' ]
+                port2 = link[ 'port2' ]
+                # Loop for two nodes
+                for i in range( 2 ):
+                    # Get port index from OVS
+                    # The index extracted from port name may be inconsistent with ONOS
+                    portIndex = -1
+                    if not nodeName1 in portDict.keys():
+                        portList = self.getOVSPorts( nodeName1 )
+                        if len( portList ) == 0:
+                            main.log.warn( self.name + ": No port found on switch " + nodeName1 )
+                            return None
+                        portDict[ nodeName1 ] = portList
+                    for port in portDict[ nodeName1 ]:
+                        if port[ 'port' ] == port1:
+                            portIndex = port[ 'index' ]
+                            break
+                    if portIndex == -1:
+                        main.log.warn( self.name + ": Cannot find port index for interface {}-eth{}".format( nodeName1, port1 ) )
+                        return None
+                    if useId:
+                        node1 = 'of:' + str( switches[ nodeName1 ][ 'dpid' ] )
+                        node2 = 'of:' + str( switches[ nodeName2 ][ 'dpid' ] )
+                    else:
+                        node1 = nodeName1
+                        node2 = nodeName2
+                    if not node1 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()
+                    graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port':portIndex }
+                    # Swap two nodes/ports
+                    nodeName1, nodeName2 = nodeName2, nodeName1
+                    port1, port2 = port2, port1
+            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 Exception:
+            main.log.exception( self.name + ": Uncaught exception" )
+            return None
+
     def update( self ):
         """
            updates the port address and status information for
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index 1ba9d0a..32a08e9 100755
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -23,6 +23,7 @@
 import time
 import os
 from drivers.common.clidriver import CLI
+from core.graph import Graph
 
 
 class OnosCliDriver( CLI ):
@@ -34,6 +35,7 @@
         self.name = None
         self.home = None
         self.handle = None
+        self.graph = Graph()
         super( CLI, self ).__init__()
 
     def connect( self, **connectargs ):
@@ -4855,3 +4857,72 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+
+    def getGraphDict( self, timeout=60, includeHost=False ):
+        """
+        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 includeHost == True, all hosts (and host-switch links) will be included
+        in topology data.
+        """
+        graphDict = {}
+        try:
+            links = self.links()
+            links = json.loads( links )
+            devices = self.devices()
+            devices = json.loads( devices )
+            idToDevice = {}
+            for device in devices:
+                idToDevice[ device[ 'id' ] ] = device
+            if includeHost:
+                hosts = self.hosts()
+                # FIXME: support 'includeHost' argument
+            for link in links:
+                nodeA = link[ 'src' ][ 'device' ]
+                nodeB = link[ 'dst' ][ 'device' ]
+                assert idToDevice[ nodeA ][ 'available' ] and idToDevice[ nodeB ][ 'available' ]
+                if not nodeA in graphDict.keys():
+                    graphDict[ nodeA ] = { 'edges':{},
+                                           'dpid':idToDevice[ nodeA ][ 'id' ][3:],
+                                           'type':idToDevice[ nodeA ][ 'type' ],
+                                           'available':idToDevice[ nodeA ][ 'available' ],
+                                           'role':idToDevice[ nodeA ][ 'role' ],
+                                           'mfr':idToDevice[ nodeA ][ 'mfr' ],
+                                           'hw':idToDevice[ nodeA ][ 'hw' ],
+                                           'sw':idToDevice[ nodeA ][ 'sw' ],
+                                           'serial':idToDevice[ nodeA ][ 'serial' ],
+                                           'chassisId':idToDevice[ nodeA ][ 'chassisId' ],
+                                           'annotations':idToDevice[ nodeA ][ 'annotations' ]}
+                else:
+                    # Assert nodeB is not connected to any current links of nodeA
+                    assert nodeB not in graphDict[ nodeA ][ 'edges' ].keys()
+                graphDict[ nodeA ][ 'edges' ][ nodeB ] = { 'port':link[ 'src' ][ 'port' ],
+                                                           'type':link[ 'type' ],
+                                                           'state':link[ 'state' ] }
+            return graphDict
+        except ( TypeError, ValueError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        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 )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return None
diff --git a/TestON/install.sh b/TestON/install.sh
index fbbd5a5..dee85c9 100755
--- a/TestON/install.sh
+++ b/TestON/install.sh
@@ -63,10 +63,10 @@
 echo "Installing TestON dependencies"
 if [ "$DIST" = "Fedora" ]; then
     # Fedora may have vlan enabled by default. Still need to confirm and update later
-    $install python-pip build-essential python-dev pep8 arping
+    $install python-pip build-essential python-dev pep8 arping python3-requests
     $pipinstall pexpect==3.2 configobj==4.7.2 numpy
 else
-    $install python-pip build-essential python-dev pep8 vlan arping
+    $install python-pip build-essential python-dev pep8 vlan arping python3-requests
     $pipinstall pexpect==3.2 configobj==4.7.2 numpy
 fi
 
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
index 314bde9..7b07758 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
@@ -32,6 +32,7 @@
         """
         import time
         from threading import Lock, Condition
+        from core.graph import Graph
         from tests.CHOTestMonkey.dependencies.elements.ONOSElement import Controller
         from tests.CHOTestMonkey.dependencies.EventGenerator import EventGenerator
         from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduler
@@ -58,6 +59,7 @@
             if main.params[ 'EVENT' ][ eventName ][ 'status' ] == 'on':
                 main.enabledEvents[ int( main.params[ 'EVENT' ][ eventName ][ 'typeIndex' ] ) ] = eventName
         print main.enabledEvents
+        main.graph = Graph()
         main.eventScheduler = EventScheduler()
         main.eventGenerator = EventGenerator()
         main.variableLock = Lock()
@@ -246,7 +248,7 @@
         startStatus = main.Mininet1.startNet( topoFile = topoPath )
         main.mininetSwitches = main.Mininet1.getSwitches()
         main.mininetHosts = main.Mininet1.getHosts()
-        main.mininetLinks = main.Mininet1.getLinks()
+        main.mininetLinks = main.Mininet1.getLinks( timeout=60 )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=startStatus,
                                  onpass="Start Mininet topology test PASS",
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py b/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py
index de32193..dd53238 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventGenerator.py
@@ -5,7 +5,6 @@
 from threading import Lock, Condition
 from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
 from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
-from tests.CHOTestMonkey.dependencies.GraphHelper import GraphHelper
 
 class MessageType:
     def __init__( self ):
@@ -169,21 +168,10 @@
             main.log.warn( "Event Generator - Too many arguments for randomLinkToggle: %s" % ( args ) )
         else:
             downUpAvgInterval = int( args[ 0 ] )
-        with main.variableLock:
-            graphHelper = GraphHelper()
-            availableLinks = graphHelper.getNonCutEdges()
-            if len( availableLinks ) == 0:
-                main.log.warn( "All links are cut edges, aborting event" )
-                return
-            link = random.sample( availableLinks, 1 )
-
         self.eventGeneratorLock.acquire()
         main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
                                            scheduleMethod,
-                                           [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
-        with main.variableLock:
-            link[ 0 ].setPendingDown()
-            link[ 0 ].backwardLink.setPendingDown()
+                                           [ 'random', 'random' ] )
         sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
         main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
         self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
@@ -199,7 +187,7 @@
             time.sleep( downUpInterval )
         main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
                                            scheduleMethod,
-                                           [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
+                                           [ 'random', 'random' ] )
         main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
         self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
         if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
@@ -227,24 +215,12 @@
             linkGroupSize = int( args[ 0 ] )
             downDownAvgInterval = int( args[ 1 ] )
             downUpAvgInterval = int( args[ 2 ] )
-        downLinks = []
         for i in range( 0, linkGroupSize ):
-            with main.variableLock:
-                graphHelper = GraphHelper()
-                availableLinks = graphHelper.getNonCutEdges()
-                if len( availableLinks ) == 0:
-                    main.log.warn( "All links are cut edges, aborting event" )
-                    continue
-                link = random.sample( availableLinks, 1 )
             if i == 0:
                 self.eventGeneratorLock.acquire()
             main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
                                                scheduleMethod,
-                                               [ link[ 0 ].deviceA.name, link[ 0 ].deviceB.name ] )
-            with main.variableLock:
-                link[ 0 ].setPendingDown()
-                link[ 0 ].backwardLink.setPendingDown()
-            downLinks.append( link[ 0 ] )
+                                               [ 'random', 'random' ] )
             sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
             self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
@@ -267,10 +243,10 @@
         else:
             time.sleep( downUpInterval )
 
-        for link in downLinks:
+        for i in range( 0, linkGroupSize ):
             main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
                                                scheduleMethod,
-                                               [ link.deviceA.name, link.deviceB.name ] )
+                                               [ 'random', 'random' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
             self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
@@ -301,20 +277,10 @@
             main.log.warn( "Event Generator - Too many arguments for randomDeviceToggle: %s" % ( args ) )
         else:
             downUpAvgInterval = int( args[ 0 ] )
-        with main.variableLock:
-            graphHelper = GraphHelper()
-            availableDevices = graphHelper.getNonCutVertices()
-            if len( availableDevices ) == 0:
-                main.log.warn( "All devices are Cut vertices, aborting event" )
-                return
-            device = random.sample( availableDevices, 1 )
-
         self.eventGeneratorLock.acquire()
         main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
                                            scheduleMethod,
-                                           [ device[ 0 ].name ] )
-        with main.variableLock:
-            device[ 0 ].setPendingDown()
+                                           [ 'random' ] )
         sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
         main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
         self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
@@ -330,7 +296,7 @@
             time.sleep( downUpInterval )
         main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
                                            scheduleMethod,
-                                           [ device[ 0 ].name ] )
+                                           [ 'random' ] )
         main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
         self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
         if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
@@ -358,23 +324,12 @@
             deviceGroupSize = int( args[ 0 ] )
             downDownAvgInterval = int( args[ 1 ] )
             downUpAvgInterval = int( args[ 2 ] )
-        downDevices = []
         for i in range( 0, deviceGroupSize ):
-            with main.variableLock:
-                graphHelper = GraphHelper()
-                availableDevices = graphHelper.getNonCutVertices()
-                if len( availableDevices ) == 0:
-                    main.log.warn( "All devices are cut vertices, aborting event" )
-                    continue
-                device = random.sample( availableDevices, 1 )
             if i == 0:
                 self.eventGeneratorLock.acquire()
             main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
                                                scheduleMethod,
-                                               [ device[ 0 ].name ] )
-            with main.variableLock:
-                device[ 0 ].setPendingDown()
-            downDevices.append( device[ 0 ] )
+                                               [ 'random' ] )
             sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
             self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
@@ -397,10 +352,10 @@
         else:
             time.sleep( downUpInterval )
 
-        for device in downDevices:
+        for i in range( 0, deviceGroupSize ):
             main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
                                                scheduleMethod,
-                                               [ device.name ] )
+                                               [ 'random' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
             self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
@@ -445,9 +400,7 @@
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
             sleepTime = int( main.params[ 'EVENT' ][ 'installAllHostIntents' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
 
@@ -482,9 +435,7 @@
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
             sleepTime = int( main.params[ 'EVENT' ][ 'removeAllHostIntents' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
 
@@ -519,9 +470,7 @@
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
             sleepTime = int( main.params[ 'EVENT' ][ 'installAllPointIntents' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
 
@@ -556,8 +505,6 @@
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
             sleepTime = int( main.params[ 'EVENT' ][ 'removeAllPointIntents' ][ 'sleepBeforeCheck' ] )
             main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_INTENT, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_FLOW, EventScheduleMethod().RUN_NON_BLOCK )
-            main.eventScheduler.scheduleEvent( EventType().CHECK_TRAFFIC, EventScheduleMethod().RUN_NON_BLOCK )
+            self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
             if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
                 main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py b/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py
index 7e4c95c..99ead73 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventScheduler.py
@@ -172,7 +172,7 @@
             if len( self.pendingEvents ) > 0:
                 events += str( self.pendingEvents[ -1 ].typeIndex )
         events += "]"
-        main.log.debug( "Event Scheduler - Events: " + events )
+        #main.log.debug( "Event Scheduler - Events: " + events )
 
     def isAvailable( self ):
         with self.pendingEventsCondition:
diff --git a/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py b/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py
index 2a18ac7..b3618dd 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/elements/NetworkElement.py
@@ -18,9 +18,6 @@
     def isRemoved( self ):
         return self.status == 'removed'
 
-    def setPendingDown( self ):
-        self.status = 'pending_down'
-
     def setRemoved( self ):
         self.status = 'removed'
 
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
index 7f0ca7c..de5fd48 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
@@ -194,10 +194,14 @@
         controller = main.controllers[ self.CLIIndex - 1 ]
         with controller.CLILock:
             # TODO: handle the case that multiple hosts attach to one device
+            srcMAC = ""
+            dstMAC = ""
+            if len( self.deviceA.hosts ) > 0:
+                srcMAC = self.deviceA.hosts[ 0 ].mac
+            if len( self.deviceB.hosts ) > 0:
+                dstMAC = self.deviceB.hosts[ 0 ].mac
             id = controller.CLI.addPointIntent( self.deviceA.dpid, self.deviceB.dpid,
-                                                1, 1, '',
-                                                self.deviceA.hosts[ 0 ].mac,
-                                                self.deviceB.hosts[ 0 ].mac )
+                                                1, 1, '', srcMAC, dstMAC )
         if id == None:
             main.log.warn( self.typeString + " - add point intent failed" )
             return EventStates().FAIL
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
index 76722f0..fcbf23d 100755
--- a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -99,6 +99,8 @@
                 if not host.isDown() and not host.isRemoved():
                     upHostNum += 1
         clusterNum = 1
+        with main.mininetLock:
+            graphDictMininet = main.Mininet1.getGraphDict( useId=True )
         for controller in main.controllers:
             if controller.isUp():
                 with controller.CLILock:
@@ -106,8 +108,14 @@
                     #if not topoState:
                     #    main.log.warn( "Topo Check - link or device number discoverd by ONOS%s is incorrect" % ( controller.index ) )
                     #    checkResult = EventStates().FAIL
-                    # Check links
+                    # Compare ONOS and Mininet topologies
+                    graphDictONOS = controller.CLI.getGraphDict()
+                    compareResult = main.graph.compareGraphs( graphDictONOS, graphDictMininet )
+                    if not compareResult:
+                        checkResult = EventStates().FAIL
+                        main.log.warn( "Topo Check - ONOS and Mininet topologies do not match" )
                     try:
+                        # Check links
                         links = controller.CLI.links()
                         links = json.loads( links )
                         if not len( links ) == upLinkNum:
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
index 7247ecf..8ec62a1 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -4,7 +4,6 @@
 """
 from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
 from tests.CHOTestMonkey.dependencies.elements.NetworkElement import NetworkElement, Device, Host, Link
-from tests.CHOTestMonkey.dependencies.GraphHelper import GraphHelper
 
 class LinkEvent( Event ):
     def __init__( self ):
@@ -28,18 +27,16 @@
                 main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
                 return EventStates().ABORT
             if args[ 0 ] == 'random' or args[ 1 ] == 'random':
-                import random
                 if self.typeIndex == EventType().NETWORK_LINK_DOWN:
-                    with main.variableLock:
-                        graphHelper = GraphHelper()
-                        availableLinks = graphHelper.getNonCutEdges()
-                        if len( availableLinks ) == 0:
-                            main.log.warn( "All links are cut edges, aborting event" )
-                            return EventStates().ABORT
-                        linkList = random.sample( availableLinks, 1 )
-                        self.linkA = linkList[ 0 ]
-                        self.linkB = linkList[ 0 ].backwardLink
+                    with main.mininetLock:
+                        linkRandom = main.Mininet1.getLinkRandom()
+                    if linkRandom == None:
+                        main.log.warn( "No link available, aborting event" )
+                        return EventStates().ABORT
+                    args[ 0 ] = linkRandom[ 0 ]
+                    args[ 1 ] = linkRandom[ 1 ]
                 elif self.typeIndex == EventType().NETWORK_LINK_UP:
+                    import random
                     with main.variableLock:
                         downLinks = []
                         for link in main.links:
@@ -54,7 +51,7 @@
             elif args[ 0 ] == args[ 1 ]:
                 main.log.warn( "%s - invalid arguments: %s" % ( self.typeString, args ) )
                 return EventStates().ABORT
-            else:
+            if self.linkA == None or self.linkB == None:
                 for link in main.links:
                     if link.deviceA.name == args[ 0 ] and link.deviceB.name == args[ 1 ]:
                         self.linkA = link
@@ -88,9 +85,13 @@
                 main.log.warn( "Link Down - link has been removed" )
                 return EventStates().ABORT
         with main.mininetLock:
+            '''
             result = main.Mininet1.link( END1=self.linkA.deviceA.name,
                                          END2=self.linkA.deviceB.name,
                                          OPTION="down")
+            '''
+            result = main.Mininet1.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
@@ -118,9 +119,13 @@
                 main.log.warn( "Link Up - link has been removed" )
                 return EventStates().ABORT
         with main.mininetLock:
+            '''
             result = main.Mininet1.link( END1=self.linkA.deviceA.name,
                                          END2=self.linkA.deviceB.name,
                                          OPTION="up")
+            '''
+            result = main.Mininet1.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
@@ -152,14 +157,12 @@
             if args[ 0 ] == 'random':
                 import random
                 if self.typeIndex == EventType().NETWORK_DEVICE_DOWN:
-                    with main.variableLock:
-                        graphHelper = GraphHelper()
-                        availableDevices = graphHelper.getNonCutVertices()
-                        if len( availableDevices ) == 0:
-                            main.log.warn( "All devices are cut vertices, aborting event" )
-                            return EventStates().ABORT
-                        deviceList = random.sample( availableDevices, 1 )
-                        self.device = deviceList[ 0 ]
+                    with main.mininetLock:
+                        switchRandom = main.Mininet1.getSwitchRandom()
+                    if switchRandom == None:
+                        main.log.warn( "No switch available, aborting event" )
+                        return EventStates().ABORT
+                    args[ 0 ] = switchRandom
                 elif self.typeIndex == EventType().NETWORK_DEVICE_UP:
                     with main.variableLock:
                         removedDevices = []
@@ -171,7 +174,7 @@
                             return EventStates().ABORT
                         deviceList = random.sample( removedDevices, 1 )
                         self.device = deviceList[ 0 ]
-            else:
+            if self.device == None:
                 for device in main.devices:
                     if device.name == args[ 0 ]:
                         self.device = device
diff --git a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.params b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.params
index 1a58556..a757a48 100755
--- a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.params
@@ -1,6 +1,6 @@
 <PARAMS>
 
-    <testcases>1,2,3</testcases>
+    <testcases>1,2,3,4,5,6</testcases>
 
     <SCALE>
         <size>3</size>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.py b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.py
index 77e90e3..c69645f 100755
--- a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.py
@@ -1,10 +1,12 @@
 # This test should always succeed. it runs cases 1,2,3
-#CASE1: 2x2 Leaf-Spine topo and test IP connectivity
-#CASE2: 4x4 topo + IP connectivity test
-#CASE3: 2x2 topo + 3-node ONOS CLUSTER + IP connectivity test
+# CASE1: 2x2 Leaf-Spine topo and test IP connectivity
+# CASE2: 4x4 topo + IP connectivity test
+# CASE3: Single switch topo + IP connectivity test
+# CASE4: 2x2 topo + 3-node ONOS CLUSTER + IP connectivity test
+# CASE5: 4x4 topo + 3-node ONOS CLUSTER + IP connectivity test
+# CASE6: Single switch + 3-node ONOS CLUSTER + IP connectivity test
 
 class SRSanity:
-
     def __init__( self ):
         self.default = ''
 
@@ -14,26 +16,27 @@
         Start 2x2 Leaf-Spine topology
         Pingall
         """
-        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
-        if not hasattr(main,'apps'):
-            run.initTest(main)
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
 
         description = "Bridging and Routing sanity test with 2x2 Leaf-spine "
         main.case( description )
 
         main.cfgName = '2x2'
         main.numCtrls = 1
-        run.installOnos(main)
-        run.startMininet(main, 'cord_fabric.py')
-        #pre-configured routing and bridging test
-        run.checkFlows(main, flowCount=116)
-        run.pingAll(main, "CASE1")
-        #TODO Dynamic config of hosts in subnet
-        #TODO Dynamic config of host not in subnet
-        #TODO Dynamic config of vlan xconnect
-        #TODO Vrouter integration
-        #TODO Mcast integration
-        run.cleanup(main)
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py' )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=116 )
+        run.pingAll( main, "CASE1" )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
 
     def CASE2( self, main ):
         """
@@ -41,46 +44,126 @@
         Start 4x4 Leaf-Spine topology
         Pingall
         """
-        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
-        if not hasattr(main,'apps'):
-            run.initTest(main)
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
         description = "Bridging and Routing sanity test with 4x4 Leaf-spine "
         main.case( description )
         main.cfgName = '4x4'
         main.numCtrls = 1
-        run.installOnos(main)
-        run.startMininet(main, 'cord_fabric.py', args="--leaf=4 --spine=4")
-        #pre-configured routing and bridging test
-        run.checkFlows(main, flowCount=350)
-        run.pingAll(main, 'CASE2')
-        #TODO Dynamic config of hosts in subnet
-        #TODO Dynamic config of host not in subnet
-        #TODO Dynamic config of vlan xconnect
-        #TODO Vrouter integration
-        #TODO Mcast integration
-        run.cleanup(main)
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py', args="--leaf=4 --spine=4" )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=350 )
+        run.pingAll( main, 'CASE2' )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
 
     def CASE3( self, main ):
         """
+        Sets up 1-node Onos-cluster
+        Start single switch topology
+        Pingall
+        """
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
+        description = "Bridging and Routing sanity test with single switch "
+        main.case( description )
+        main.cfgName = '0x1'
+        main.numCtrls = 1
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py', args="--leaf=1 --spine=0" )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=15 )
+        run.pingAll( main, 'CASE3' )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
+
+    def CASE4( self, main ):
+        """
         Sets up 3-node Onos-cluster
         Start 2x2 Leaf-Spine topology
         Pingall
         """
-        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
-        if not hasattr(main,'apps'):
-            run.initTest(main)
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
         description = "Bridging and Routing sanity test with 2x2 Leaf-spine "
         main.case( description )
         main.cfgName = '2x2'
         main.numCtrls = 3
-        run.installOnos(main)
-        run.startMininet(main, 'cord_fabric.py')
-        #pre-configured routing and bridging test
-        run.checkFlows(main, flowCount=116)
-        run.pingAll(main, 'CASE3')
-        #TODO Dynamic config of hosts in subnet
-        #TODO Dynamic config of host not in subnet
-        #TODO Dynamic config of vlan xconnect
-        #TODO Vrouter integration
-        #TODO Mcast integration
-        run.cleanup(main)
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py' )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=116 )
+        run.pingAll( main, 'CASE4' )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
+
+    def CASE5( self, main ):
+        """
+        Sets up 3-node Onos-cluster
+        Start 4x4 Leaf-Spine topology
+        Pingall
+        """
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
+        description = "Bridging and Routing sanity test with 4x4 Leaf-spine "
+        main.case( description )
+        main.cfgName = '4x4'
+        main.numCtrls = 3
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py', args="--leaf=4 --spine=4" )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=350 )
+        run.pingAll( main, 'CASE5' )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
+
+    def CASE6( self, main ):
+        """
+        Sets up 3-node Onos-cluster
+        Start single switch topology
+        Pingall
+        """
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \
+            Testcaselib as run
+        if not hasattr( main, 'apps' ):
+            run.initTest( main )
+        description = "Bridging and Routing sanity test with single switch "
+        main.case( description )
+        main.cfgName = '0x1'
+        main.numCtrls = 3
+        run.installOnos( main )
+        run.startMininet( main, 'cord_fabric.py', args="--leaf=1 --spine=0" )
+        # pre-configured routing and bridging test
+        run.checkFlows( main, minFlowCount=15 )
+        run.pingAll( main, 'CASE4' )
+        # TODO Dynamic config of hosts in subnet
+        # TODO Dynamic config of host not in subnet
+        # TODO Dynamic config of vlan xconnect
+        # TODO Vrouter integration
+        # TODO Mcast integration
+        run.cleanup( main )
diff --git a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo
index 546a023..c531f7d 100755
--- a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo
+++ b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo
@@ -42,13 +42,45 @@
             </COMPONENTS>
         </ONOScli3>
 
+        <ONOSrest1>
+            <host>OC1</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest1>
+
+        <ONOSrest2>
+            <host>OC2</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest2>
+
+        <ONOSrest3>
+            <host>OC3</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest3>
 
         <Mininet1>
             <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>5</connect_order>
+            <connect_order>7</connect_order>
             <COMPONENTS>
                 <home>~/mininet/custom/</home>
             </COMPONENTS>
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/0x1.json b/TestON/tests/USECASE/SegmentRouting/dependencies/0x1.json
new file mode 100755
index 0000000..8232036
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/0x1.json
@@ -0,0 +1,46 @@
+{
+    "ports" : {
+    "of:0000000000000001/1" : {
+        "interfaces" : [
+        {
+            "ips" : [ "10.0.1.254/24" ]
+        }
+        ]
+    },
+    "of:0000000000000001/2" : {
+        "interfaces" : [
+        {
+            "ips" : [ "10.0.1.254/24" ]
+        }
+        ]
+    }
+    },
+    "devices" : {
+        "of:0000000000000001" : {
+            "basic":{ "driver" : "ofdpa-cpqd" },
+            "segmentrouting" : {
+                "name" : "Leaf-R1",
+                "nodeSid" : 1,
+                "routerIp" : "192.168.0.1",
+                "routerMac" : "10:00:00:00:00:01",
+                "isEdgeRouter" : true,
+                "adjacencySids" : []
+            }
+        }
+    },
+    "hosts" : {
+        "00:00:00:00:00:01/-1" : {
+            "basic": {
+                "ips": ["10.0.1.1"],
+                "location": "of:0000000000000001/1"
+            }
+        },
+        "00:00:00:00:00:02/-1" : {
+            "basic": {
+                "ips": ["10.0.1.2"],
+                "location": "of:0000000000000001/2"
+            }
+        }
+    }
+
+}
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
index 3bbca7d..1e31cd3 100755
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
@@ -1,6 +1,7 @@
 import os

 import imp

 import time

+import json

 

 from core import utilities

 

@@ -17,7 +18,6 @@
             - Install ONOS package

             - Build ONOS package

         """

-        main.case( "Constructing test variables and building ONOS" )

         main.step( "Constructing test variables" )

         # Test variables

         main.cellName = main.params[ 'ENV' ][ 'cellName' ]

@@ -35,10 +35,12 @@
         main.cellData = {} # for creating cell file

         main.CLIs = []

         main.ONOSip = []

+        main.RESTs= []

 

         # Assigning ONOS cli handles to a list

         for i in range( 1,  main.maxNodes + 1 ):

             main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )

+            main.RESTs.append( getattr( main, 'ONOSrest' + str( i ) ) )

             main.ONOSip.append( main.CLIs[i-1].ip_address )

         # -- INIT SECTION, ONLY RUNS ONCE -- #

         main.startUp = imp.load_source( wrapperFile1,

@@ -109,7 +111,6 @@
         for i in range( main.maxNodes ):

             main.ONOSbench.onosDie( main.ONOSip[ i ] )

         main.step( "Create and Install ONOS package" )

-        main.ONOSbench.handle.sendline( "cp "+main.dependencyPath+"/"+main.cfgName+".json ~/onos/tools/package/config/network-cfg.json")

         packageResult = main.ONOSbench.onosPackage()

 

         onosInstallResult = main.TRUE

@@ -164,6 +165,9 @@
                                  onpass="ONOS summary command succeded",

                                  onfail="ONOS summary command failed" )

 

+        with open( main.dependencyPath + "/" + main.cfgName + ".json" ) as cfg:

+            main.RESTs[main.active].setNetCfg( json.load(cfg) )

+

         if not ready:

             main.log.error( "ONOS startup failed!" )

             main.cleanup()

@@ -192,13 +196,13 @@
             main.exit()

 

     @staticmethod

-    def checkFlows( main, flowCount ):

-        main.step(" Check whether the flow count is bigger than %s" % flowCount)

-        count =  utilities.retry( main.CLIs[main.active].checkFlowCount,

-                                  main.FALSE,

-                                  kwargs={'min':flowCount},

-                                  attempts=10,

-                                  sleep=10 )

+    def checkFlows(main, minFlowCount):

+        main.step(" Check whether the flow count is bigger than %s" % minFlowCount)

+        count =  utilities.retry(main.CLIs[main.active].checkFlowCount,

+                                 main.FALSE,

+                                 kwargs={'min':minFlowCount},

+                                 attempts=10,

+                                 sleep=10)

         utilities.assertEquals( \

                 expect=True,

                 actual=(count>0),

@@ -229,7 +233,6 @@
         utilities.assert_equals( expect=main.TRUE, actual=pa,

                                  onpass="Full connectivity successfully tested",

                                  onfail="Full connectivity failed" )

-        ##FIXME choose valid onos instead of 0

         main.ONOSbench.dumpFlows( main.ONOSip[main.active],

                                   main.logdir, "flowsOn" + tag)

         main.ONOSbench.dumpGroups( main.ONOSip[main.active],

@@ -245,11 +248,9 @@
         main.linkSleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )

         main.step( "Kill link between %s and %s" %(end1, end2))

         LinkDown = main.Mininet1.link( END1=end1, END2=end2, OPTION="down" )

-        main.log.info( "Waiting %s seconds for link up to be discovered" % (main.linkSleep) )

-        # TODO Maybe parameterize number of expected links

+        main.log.info( "Waiting %s seconds for link down to be discovered" % main.linkSleep )

         time.sleep( main.linkSleep )

-        ##FIXME CLI for active node instead of 0

-        topology =  utilities.retry( main.CLIs[main.active].checkStatus,

+        topology =  utilities.retry( main.CLIs[ main.active ].checkStatus,

                                      main.FALSE,

                                      kwargs={'numoswitch':switches, 'numolink':links},

                                      attempts=10,

@@ -265,7 +266,7 @@
         Params:

             end1,end2: identify the end switches, ex.: 'leaf1', 'spine1'

             dpid1, dpid2: dpid of the end switches respectively, ex.: 'of:0000000000000002'

-            port1, port2: respective port of the end switchs that connects to the link, ex.:'1'

+            port1, port2: respective port of the end switches that connects to the link, ex.:'1'

             switches, links: number of expected switches and links after linkDown, ex.: '4', '6'

         Kill a link and verify ONOS can see the proper link change

         """

@@ -276,7 +277,7 @@
             count+=0

             main.Mininet1.link( END1=end1, END2=end2, OPTION="up" )

             main.Mininet1.link( END2=end1, END1=end2, OPTION="up" )

-            main.log.info( "Waiting %s seconds for link up to be discovered" % (main.linkSleep) )

+            main.log.info( "Waiting %s seconds for link up to be discovered" % main.linkSleep )

             time.sleep( main.linkSleep )

             main.CLIs[main.active].portstate( dpid=dpid1, port=port1 )

             main.CLIs[main.active].portstate( dpid=dpid2, port=port2 )

@@ -293,7 +294,7 @@
     def killSwitch( main, switch, switches, links ):

         """

         Params: switches, links: number of expected switches and links after SwitchDown, ex.: '4', '6'

-        Kill a switch and verify ONOS can see the proper change

+        Completely kill a switch and verify ONOS can see the proper change

         """

         main.switchSleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )

         main.step( "Kill " + switch )

@@ -338,9 +339,9 @@
         Copies ONOS log

         """

         main.Mininet1.stopNet()

-        main.ONOSbench.scp( main.ONOScli1 ,"/opt/onos/log/karaf.log",

+        main.ONOSbench.scp( main.ONOScli1, "/opt/onos/log/karaf.log",

                            "/tmp/karaf.log", direction="from" )

-        main.ONOSbench.cpLogsToDir("/tmp/karaf.log",main.logdir,

-                                   copyFileName="karaf.log."+main.cfgName)

-        for i in range(main.numCtrls):

+        main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,

+                                   copyFileName="karaf.log."+main.cfgName )

+        for i in range( main.numCtrls ):

             main.ONOSbench.onosStop( main.ONOSip[i] )