Allow use of onos docker for existing tests

- Allow Cluster to pull/build onos docker
- Connect clidriver to cli runnning in docker
- Some changes for debugability in components
- To use, set the useDocker and diffCliHosts tags in the cluster
  component to True, then define parameters in the params file
- Update all SR Stratum tests to use the tost docker image
- NOTE: Since the tost-onos image doesn't have openflow installe, we are
  currently only using docker for the bmv2 and tofino switches

Change-Id: If900b0bdbf9a41b8885c692ccba18a3b1bc580cc
diff --git a/TestON/JenkinsFile/dependencies/tests.json b/TestON/JenkinsFile/dependencies/tests.json
index 215a3e1..36254c3 100644
--- a/TestON/JenkinsFile/dependencies/tests.json
+++ b/TestON/JenkinsFile/dependencies/tests.json
@@ -1311,7 +1311,7 @@
     },
     "SRDhcprelay-stratum": {
         "test": "SRDhcprelay",
-        "arguments": "--params-file SRDhcprelay.params.stratum",
+        "arguments": "--params-file SRDhcprelay.params.stratum --topo-file SRDhcprelay.topo.docker",
         "wikiName": "SR Dhcp Relay with Stratum BMv2 switches",
         "wikiFile": "SRDhcprelayWiki.txt",
         "schedules": [
@@ -1331,7 +1331,7 @@
     },
     "SRRouting-stratum": {
         "test": "SRRouting",
-        "arguments": "--params-file SRRouting.params.stratum",
+        "arguments": "--params-file SRRouting.params.stratum --topo-file SRRouting.topo.docker",
         "wikiName": "SR Routing with Stratum BMv2 switches",
         "wikiFile": "SRRoutingWiki.txt",
         "schedules": [
@@ -1351,7 +1351,7 @@
     },
     "SRBridging-stratum": {
         "test": "SRBridging",
-        "arguments": "--params-file SRBridging.params.stratum",
+        "arguments": "--params-file SRBridging.params.stratum --topo-file SRBridging.topo.docker",
         "wikiName": "SR Bridging with Stratum BMv2 switches",
         "wikiFile": "SRBridgingWiki.txt",
         "schedules": [
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index af4309d..d3f843d 100755
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -26,6 +26,7 @@
 import requests
 import types
 import sys
+import time
 
 from drivers.common.api.controllerdriver import Controller
 
@@ -53,7 +54,7 @@
             else:
                 main.log.info( self.name + ": ip set to " + self.ip_address )
         except KeyError:
-            main.log.info( "Invalid host name," +
+            main.log.info( self.name + ": Invalid host name," +
                            "defaulting to 'localhost' instead" )
             self.ip_address = 'localhost'
         except Exception as inst:
@@ -99,21 +100,21 @@
         # TODO: Do we need to allow for other protocols besides http?
         # ANSWER: Not yet, but potentially https with certificates
         if ip == "DEFAULT":
-            main.log.warn( "No ip given, reverting to ip from topo file" )
+            main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
             ip = self.ip_address
         if port == "DEFAULT":
-            main.log.warn( "No port given, reverting to port " +
+            main.log.warn( self.name + ": No port given, reverting to port " +
                            "from topo file" )
             port = self.port
 
         try:
             path = "http://" + str( ip ) + ":" + str( port ) + base + url
             if self.user_name and self.pwd:
-                main.log.info( "user/passwd is: " + self.user_name + "/" + self.pwd )
+                main.log.info( self.name + ": user/passwd is: " + self.user_name + "/" + self.pwd )
                 auth = ( self.user_name, self.pwd )
             else:
                 auth = None
-            main.log.info( "Sending request " + path + " using " +
+            main.log.info( self.name + ": Sending request " + path + " using " +
                            method.upper() + " method." )
             response = requests.request( method.upper(),
                                          path,
@@ -142,10 +143,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( url="/intents", ip = ip, port = port )
@@ -157,8 +158,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -187,10 +188,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
@@ -202,8 +203,8 @@
                     a = json.loads( output )
                     return a
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -223,10 +224,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( url="/applications", ip = ip, port = port )
@@ -238,8 +239,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -264,44 +265,62 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             query = "/" + str( appName ) + "/active"
-            response = self.send( method="POST",
-                                  url="/applications" + query,
-                                  ip = ip, port = port )
-            if response:
-                output = response[ 1 ]
-                app = json.loads( output )
-                if 200 <= response[ 0 ] <= 299:
-                    if check:
-                        if app.get( 'state' ) == 'ACTIVE':
-                            main.log.info( self.name + ": " + appName +
-                                           " application" +
-                                           " is in ACTIVE state" )
-                            return main.TRUE
+            retry = 0
+            retCode = main.TRUE
+            while retry < 50:
+                if retry > 0:
+                    main.log.warn( self.name + ": Retrying " + query + " for the " + str( retry ) + " time" )
+
+                retry += 1
+                response = self.send( method="POST",
+                                      debug=True,
+                                      url="/applications" + query,
+                                      ip = ip, port = port )
+                if response:
+                    output = response[ 1 ]
+                    if 200 <= response[ 0 ] <= 299:
+                        if check:
+                            app = json.loads( output )
+                            if app.get( 'state' ) == 'ACTIVE':
+                                main.log.info( self.name + ": " + appName +
+                                               " application" +
+                                               " is in ACTIVE state" )
+                                appHealth = self.getAppHealth( appName=appName, ip=ip, port=port )
+                                if "ready" == json.loads( appHealth[1] ).get( 'message' ):
+                                    return main.TRUE
+                                else:
+                                    return main.FALSE
+                            else:
+                                main.log.error( self.name + ": " + appName +
+                                                " application" + " is in " +
+                                                app.get( 'state' ) + " state" )
+                                retCode = main.FALSE
                         else:
-                            main.log.error( self.name + ": " + appName +
-                                            " application" + " is in " +
-                                            app.get( 'state' ) + " state" )
-                            return main.FALSE
+                            main.log.warn( self.name + ": Skipping " + appName +
+                                           "application check" )
+                            return main.TRUE
                     else:
-                        main.log.warn( "Skipping " + appName +
-                                       "application check" )
-                        return main.TRUE
-                else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
-                    return main.FALSE
+                        main.log.error( "Error with REST request, response was: %s: %s" %
+                                        ( response[ 0 ], response[ 1 ] ) )
+                        retCode = main.FALSE
+                time.sleep( 30 )
+                return retCode
+        except ( ValueError ):
+            main.log.exception( self.name + ": Error parsing json" )
+            return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
             return None
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
+            main.log.debug( self.name + ": " + response )
             main.cleanAndExit()
 
     def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
@@ -320,10 +339,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             query = "/" + str( appName ) + "/active"
@@ -347,12 +366,12 @@
                                             app.get( 'state' ) + " state" )
                             return main.FALSE
                     else:
-                        main.log.warn( "Skipping " + appName +
+                        main.log.warn( self.name + ": Skipping " + appName +
                                        "application check" )
                         return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -373,12 +392,12 @@
             Returns main.FALSE if error on requests; Returns None for exception
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             query = "/" + str( appName )
@@ -388,8 +407,8 @@
                 if 200 <= response[ 0 ] <= 299:
                     return response
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -398,6 +417,74 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
+    def getAppHealth( self, appName, ip="DEFAULT",
+                      port="DEFAULT" ):
+        """
+        Decription:
+            Gets the health of the given application
+        Required:
+            str name - Name of onos application
+        Returns:
+            Returns a dictionary of information ONOS application in string type;
+            Returns main.FALSE if error on requests; Returns None for exception
+        """
+        try:
+            response = None
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            response = self.send( url="/applications/%s/health" % str( appName ),
+                                  ip = ip, port = port )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    return response
+                else:
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
+                    return main.FALSE
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def getAllAppHealth( self, retries=1, wait=30 , ip="DEFAULT",
+                         port="DEFAULT" ):
+        """
+        Description:
+            Gets the health of all activated apps
+        Required:
+        Optional:
+            retries - The number of tries to return before returning
+            wait - Time to wait in between retries
+        """
+        try:
+            responses = main.TRUE
+            if ip == "DEFAULT":
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( self.name + ": No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            apps = self.apps()
+            for app in json.loads(apps):
+                appName = app.get( "name" )
+                response = self.getAppHealth( appName=appName, ip=ip, port=port )
+                responses = main.TRUE and "ready" == json.loads( response[1] ).get( "message" )
+            return responses
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
     def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
                        ip="DEFAULT", port="DEFAULT", vlanId="" ):
         """
@@ -425,12 +512,12 @@
             if vlanId:
                 intentJson[ 'selector' ][ 'criteria' ].append( { "type": "VLAN_VID",
                                                                  "vlanId": vlanId } )
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( method="POST",
@@ -443,8 +530,8 @@
                                    " and host: " + hostIdTwo )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
 
         except ( AttributeError, TypeError ):
@@ -534,9 +621,6 @@
                                               "types": [ "OPTICAL" ],
                                               "inclusive": "false" } ] }
 
-            # if protected:
-            #     intentJson['constraints'].append( { "type": "Protection", "types": ["Protection"], "inclusive": "true" } )
-
             if ethType == "IPV4":
                 intentJson[ 'selector' ][ 'criteria' ].append( {
                                                          "type": "ETH_TYPE",
@@ -581,12 +665,12 @@
 
             # TODO: Bandwidth and Lambda will be implemented if needed
 
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( method="POST",
@@ -602,8 +686,8 @@
                                    " and egress: " + egressDevice + " devices" )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
 
         except ( AttributeError, TypeError ):
@@ -743,12 +827,12 @@
 
             # TODO: Bandwidth and Lambda will be implemented if needed
 
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( method="POST",
@@ -764,7 +848,7 @@
                                    " and egress: " + str( egressDeviceList ) + " devices" )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " + str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" % ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
             else:
                 main.log.error( "REST request has no response." )
@@ -784,12 +868,12 @@
             Returns None for exception
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
@@ -800,8 +884,8 @@
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -810,7 +894,7 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
-    def getIntentsId( self ):
+    def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
         """
         Description:
             Gets all intents ID using intents function
@@ -819,11 +903,11 @@
         """
         try:
             intentIdList = []
-            intentsJson = json.loads( self.intents() )
+            intentsJson = json.loads( self.intents( ip=ip, port=port ) )
             for intent in intentsJson:
                 intentIdList.append( intent.get( 'id' ) )
             if not intentIdList:
-                main.log.debug( "Cannot find any intents" )
+                main.log.debug( self.name + ": Cannot find any intents" )
                 return main.FALSE
             else:
                 return intentIdList
@@ -894,10 +978,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( url="/hosts", ip = ip, port = port )
@@ -909,8 +993,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -938,10 +1022,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             query = "/" + mac + "/" + vlan
@@ -956,8 +1040,8 @@
                     hostId = json.loads( output ).get( 'id' )
                     return hostId
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -977,10 +1061,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( url="/topology", ip = ip, port = port )
@@ -991,8 +1075,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1013,10 +1097,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             response = self.send( url="/devices", ip = ip, port = port )
@@ -1028,8 +1112,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1067,7 +1151,7 @@
                     if intentsId == intent[ 'id' ]:
                         state = intent[ 'state' ]
                         return state
-                main.log.info( "Cannot find intent ID" + str( intentsId ) +
+                main.log.info( self.name + ": Cannot find intent ID" + str( intentsId ) +
                                " on the list" )
                 return state
             elif isinstance( intentsId, types.ListType ):
@@ -1081,10 +1165,10 @@
                             dictList.append( stateDict )
                             break
                 if len( intentsId ) != len( dictList ):
-                    main.log.info( "Cannot find some of the intent ID state" )
+                    main.log.info( self.name + ": Cannot find some of the intent ID state" )
                 return dictList
             else:
-                main.log.info( "Invalid intents ID entry" )
+                main.log.info( self.name + ": Invalid intents ID entry" )
                 return None
 
         except ( AttributeError, TypeError ):
@@ -1126,7 +1210,7 @@
             if isinstance( expectedState, types.StringType ):
                 for intents in intentsDict:
                     if intents.get( 'state' ) != expectedState:
-                        main.log.debug( self.name + " : Intent ID - " +
+                        main.log.debug( self.name + ": Intent ID - " +
                                         intents.get( 'id' ) +
                                         " actual state = " +
                                         intents.get( 'state' )
@@ -1138,7 +1222,7 @@
                 for intents in intentsDict:
                     if not any( state == intents.get( 'state' ) for state in
                                 expectedState ):
-                        main.log.debug( self.name + " : Intent ID - " +
+                        main.log.debug( self.name + ": Intent ID - " +
                                         intents.get( 'id' ) +
                                         " actual state = " +
                                         intents.get( 'state' ) +
@@ -1173,10 +1257,10 @@
             output = None
             url = "/flows"
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             if subjectKey and not subjectClass:
@@ -1194,8 +1278,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1217,10 +1301,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/flows/" + deviceId
@@ -1236,8 +1320,8 @@
                     b = json.dumps( a )
                     return b
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1265,13 +1349,13 @@
 
         try:
             if debug:
-                main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
-            output = None
+                main.log.debug( self.name + ": Adding flow: " + self.pprint( flowJson ) )
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/flows/" + deviceId
@@ -1284,8 +1368,8 @@
                                    "in device: " + str( deviceId ) )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except NotImplementedError as e:
             raise e  # Inform the caller
@@ -1438,12 +1522,12 @@
             Returns main.FALSE, Returns None on error
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
@@ -1454,8 +1538,8 @@
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1504,10 +1588,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/network/configuration"
@@ -1526,11 +1610,11 @@
                     return b
                 elif response[ 0 ] == 404:
                     main.log.error( "Requested configuration doesn't exist: " +
-                                    str( response ) )
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return {}
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1551,12 +1635,12 @@
 
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/network/configuration"
@@ -1574,8 +1658,8 @@
                     main.log.info( self.name + ": Successfully POST cfg" )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1596,12 +1680,12 @@
 
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/network/configuration"
@@ -1618,8 +1702,8 @@
                     main.log.info( self.name + ": Successfully delete cfg" )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1780,7 +1864,7 @@
             # pprint(flowJson)
             flowJsonList.append( flowJson )
 
-        main.log.info( "Number of flows in batch: " + str( len( flowJsonList ) ) )
+        main.log.info( self.name + ": Number of flows in batch: " + str( len( flowJsonList ) ) )
         flowJsonBatch[ 'flows' ] = flowJsonList
         # pprint(flowJsonBatch)
 
@@ -1800,17 +1884,16 @@
             The ip and port option are for the requests input's ip and port
             of the ONOS node
         """
-        import time
 
         try:
             if debug:
-                main.log.debug( "Adding flow: " + self.pprint( batch ) )
-            output = None
+                main.log.debug( self.name + ": Adding flow: " + self.pprint( batch ) )
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/flows/"
@@ -1822,8 +1905,8 @@
                 main.log.info( self.name + ": Successfully POST flow batch" )
                 return main.TRUE, response
             else:
-                main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                 return main.FALSE, response
         except NotImplementedError as e:
             raise e  # Inform the caller
@@ -1846,12 +1929,12 @@
             Returns main.FALSE, Returns None on error
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             # NOTE: REST url requires the intent id to be in decimal form
@@ -1863,8 +1946,8 @@
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -1886,10 +1969,6 @@
             topology = json.loads( topologyOutput )
             main.log.debug( topology )
             return topology
-        except pexpect.EOF:
-            main.log.error( self.name + ": EOF exception found" )
-            main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanAndExit()
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
@@ -1948,15 +2027,12 @@
             else:
                 main.log.info( output )
             return result
-        except pexpect.EOF:
-            main.log.error( self.name + ": EOF exception found" )
-            main.log.error( self.name + ":    " + self.handle.before )
-            main.cleanAndExit()
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
-    def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
+    def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId,
+                  ip="DEFAULT", port="DEFAULT", debug=False ):
         """
         Description:
             Creates a single Group for the specified device.
@@ -2006,13 +2082,13 @@
         """
         try:
             if debug:
-                main.log.debug( "Adding group: " + self.pprint( groupJson ) )
-            output = None
+                main.log.debug( self.name + ": Adding group: " + self.pprint( groupJson ) )
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/groups/" + deviceId
@@ -2025,8 +2101,8 @@
                                    "in device: " + str( deviceId ) )
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except NotImplementedError as e:
             raise e  # Inform the caller
@@ -2056,10 +2132,10 @@
         try:
             output = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             url = "/groups"
@@ -2076,8 +2152,8 @@
                     groups = json.dumps( groupsJson )
                     return groups
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, AssertionError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
@@ -2104,12 +2180,12 @@
 
         """
         try:
-            output = None
+            response = None
             if ip == "DEFAULT":
-                main.log.warn( "No ip given, reverting to ip from topo file" )
+                main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
                 ip = self.ip_address
             if port == "DEFAULT":
-                main.log.warn( "No port given, reverting to port " +
+                main.log.warn( self.name + ": No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
             query = "/" + str( deviceId ) + "/" + str( appCookie )
@@ -2119,8 +2195,8 @@
                 if 200 <= response[ 0 ] <= 299:
                     return main.TRUE
                 else:
-                    main.log.error( "Error with REST request, response was: " +
-                                    str( response ) )
+                    main.log.error( "Error with REST request, response was: %s: %s" %
+                                    ( response[ 0 ], response[ 1 ] ) )
                     return main.FALSE
         except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 682760a..47123a4 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -163,7 +163,7 @@
                     main.log.info( self.name + ": Sending sudo password" )
                     self.handle.sendline( self.pwd )
                     i = self.handle.expect( [ '%s:' % self.user_name,
-                                              self.prompt,
+                                              self.Prompt(),
                                               pexpect.EOF,
                                               pexpect.TIMEOUT ],
                                             timeout )
@@ -210,12 +210,12 @@
                     cmdString += mnCmd
                 # Send the command and check if network started
                 self.handle.sendline( "" )
-                self.handle.expect( self.prompt )
+                self.handle.expect( self.Prompt() )
                 main.log.info( "Sending '" + cmdString + "' to " + self.name )
                 self.handle.sendline( cmdString )
                 startTime = time.time()
                 while True:
-                    i = self.handle.expect( [ 'mininet>',
+                    i = self.handle.expect( [ self.mnPrompt,
                                               'Exception|Error',
                                               '\*\*\*',
                                               pexpect.EOF,
@@ -228,7 +228,7 @@
                     elif i == 1:
                         response = str( self.handle.before +
                                         self.handle.after )
-                        self.handle.expect( self.prompt )
+                        self.handle.expect( self.Prompt() )
                         response += str( self.handle.before +
                                          self.handle.after )
                         main.log.error(
@@ -1255,7 +1255,7 @@
         try:
             response = self.execute(
                 cmd='dump',
-                prompt='mininet>',
+                prompt=self.mnPrompt,
                 timeout=10 )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -1271,7 +1271,7 @@
         try:
             response = self.execute(
                 cmd='intfs',
-                prompt='mininet>',
+                prompt=self.mnPrompt,
                 timeout=10 )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -1285,7 +1285,7 @@
     def net( self ):
         main.log.info( self.name + ": List network connections" )
         try:
-            response = self.execute( cmd='net', prompt='mininet>', timeout=10 )
+            response = self.execute( cmd='net', prompt=self.mnPrompt, timeout=10 )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":     " + self.handle.before )
@@ -1298,7 +1298,7 @@
     def links( self, timeout=1000 ):
         main.log.info( self.name + ": List network links" )
         try:
-            response = self.execute( cmd='links', prompt='mininet>',
+            response = self.execute( cmd='links', prompt=self.mnPrompt,
                                      timeout=timeout )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -1506,7 +1506,7 @@
         try:
             response = self.execute(
                 cmd='nodes',
-                prompt='mininet>',
+                prompt=self.mnPrompt,
                 timeout=10 )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -1522,7 +1522,7 @@
         try:
             response = self.execute(
                 cmd='pingpair',
-                prompt='mininet>',
+                prompt=self.mnPrompt,
                 timeout=20 )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2253,7 +2253,7 @@
         """
         try:
             self.handle.sendline( '' )
-            i = self.handle.expect( [ 'mininet>', self.hostPrompt, pexpect.EOF, pexpect.TIMEOUT ],
+            i = self.handle.expect( [ self.mnPrompt, self.hostPrompt, pexpect.EOF, pexpect.TIMEOUT ],
                                     timeout=2 )
             response = main.TRUE
             if i == 0:
@@ -2292,7 +2292,7 @@
             try:
                 self.handle.sendline( "" )
                 i = self.handle.expect( [ self.mnPrompt,
-                                          self.bashPrompt,
+                                          self.Prompt(),
                                           pexpect.EOF,
                                           pexpect.TIMEOUT ],
                                         timeout )
@@ -2301,8 +2301,8 @@
                     startTime = time.time()
                     self.handle.sendline( "exit" )
                     exitRet = 1
-                    while not exitRet:
-                        exitRet = self.handle.expect( [ self.bashprompt,
+                    while exitRet:
+                        exitRet = self.handle.expect( [ self.Prompt(),
                                                         "Traceback",
                                                         "AssertionError",
                                                         self.mnPrompt ],
@@ -2315,6 +2315,7 @@
                         cmd = "sudo " + cmd
                     self.handle.sendline( cmd )
                     response = main.TRUE
+                    self.handle.expect( self.Prompt() )
 
                 elif i == 1:
                     main.log.info( " Mininet trying to exit while not " +
@@ -2327,21 +2328,21 @@
                                     "TIMEOUT" )
 
                 self.handle.sendline( "" )
-                self.handle.expect( self.prompt )
+                self.handle.expect( self.Prompt() )
                 cmd = "killall -9 dhclient dhcpd zebra bgpd"
                 if self.sudoRequired:
                     cmd = "sudo " + cmd
                 self.handle.sendline( cmd )
-                self.handle.expect( self.prompt )
+                self.handle.expect( self.Prompt() )
 
                 if fileName:
                     self.handle.sendline( "" )
-                    self.handle.expect( self.prompt )
+                    self.handle.expect( self.Prompt() )
                     cmd = "kill -9 \`ps -ef | grep \"" + fileName + "\" | grep -v grep | awk '{print $2}'\`"
                     if self.sudoRequired:
                         cmd = "sudo " + cmd
                     self.handle.sendline( cmd )
-                    self.handle.expect( self.prompt )
+                    self.handle.expect( self.Prompt() )
             except pexpect.TIMEOUT:
                 main.log.error( self.name + ": TIMEOUT exception found" )
                 main.log.error( self.name + ":     " + self.handle.before )
@@ -3898,7 +3899,7 @@
                 host = self.name
             if self.mExecDir:
                 self.handle.sendline( "cd %s" % self.mExecDir )
-                self.handle.expect( self.prompt )
+                self.handle.expect( self.Prompt() )
 
             self.handle.sendline( self.home + "/util/m " + host )
             if self.hostHome:
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index c317fb0..7a0641e 100755
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -58,6 +58,7 @@
         self.handle = None
         self.karafUser = None
         self.karafPass = None
+        self.dockerPrompt = None
         self.graph = Graph()
         super( OnosCliDriver, self ).__init__()
 
@@ -82,9 +83,12 @@
                     self.karafUser = self.options[ key ]
                 elif key == "karaf_password":
                     self.karafPass = self.options[ key ]
+                elif key == "docker_prompt":
+                    self.dockerPrompt = self.options[ key ]
             self.home = self.checkOptions( self.home, "~/onos" )
             self.karafUser = self.checkOptions( self.karafUser, self.user_name )
             self.karafPass = self.checkOptions( self.karafPass, self.pwd )
+            self.dockerPrompt = self.checkOptions( self.dockerPrompt, "~/onos#" )
 
             for key in self.options:
                 if key == 'onosIp':
@@ -143,9 +147,15 @@
                 i = self.logout()
                 if i == main.TRUE:
                     self.handle.sendline( "" )
-                    self.handle.expect( self.prompt )
-                    self.handle.sendline( "exit" )
-                    self.handle.expect( "closed" )
+                    for l in range( 3 ):
+                        p = self.handle.expect( [ self.prompt, self.dockerPrompt ] )
+                        if p == 1:
+                            self.inDocker = False
+                        self.handle.sendline( "exit" )
+                        j = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=3 )
+                        if j == 0:
+                            return response
+                    response = main.FALSE
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             response = main.FALSE
@@ -156,7 +166,7 @@
             main.log.exception( "Exception in disconnect of " + self.name )
             response = main.TRUE
         except Exception:
-            main.log.exception( self.name + ": Connection failed to the host" )
+            main.log.exception( self.name + ": disconnection failed from the host" )
             response = main.FALSE
         return response
 
@@ -171,11 +181,14 @@
         try:
             if self.handle:
                 self.handle.sendline( "" )
-                i = self.handle.expect( [ self.karafPrompt, self.prompt, pexpect.TIMEOUT ],
+                i = self.handle.expect( [ self.karafPrompt,
+                                          self.Prompt(),
+                                          pexpect.TIMEOUT ],
+
                                         timeout=10 )
                 if i == 0:  # In ONOS CLI
                     self.handle.sendline( "logout" )
-                    j = self.handle.expect( [ self.prompt,
+                    j = self.handle.expect( [ self.Prompt(),
                                               "Command not found:",
                                               pexpect.TIMEOUT ] )
                     if j == 0:  # Successfully logged out
@@ -185,7 +198,7 @@
                         # or the command timed out
                         self.handle.send( "\x04" )  # send ctrl-d
                         try:
-                            self.handle.expect( self.prompt )
+                            self.handle.expect( self.Prompt() )
                         except pexpect.TIMEOUT:
                             main.log.error( "ONOS did not respond to 'logout' or CTRL-d" )
                         return main.TRUE
@@ -272,59 +285,49 @@
         try:
             # Check if we are already in the cli
             self.handle.sendline( "" )
-            x = self.handle.expect( [
-                self.prompt, self.karafPrompt ], commandlineTimeout )
+            x = self.handle.expect( [ self.Prompt(), self.karafPrompt ], commandlineTimeout )
             if x == 1:
                 main.log.info( "ONOS cli is already running" )
                 return main.TRUE
 
             # Not in CLI so login
-            if waitForStart:
+            if self.inDocker:
+                # The Docker does not have all the wrapper scripts
+                startCliCommand = "ssh -p 8101 -o StrictHostKeyChecking=no %s@localhost" % self.karafUser
+            elif waitForStart:
                 # Wait for onos start ( onos-wait-for-start ) and enter onos cli
-                startCliCommand = "onos-wait-for-start "
+                startCliCommand = "onos-wait-for-start " + str( ONOSIp )
             else:
-                startCliCommand = "onos "
-            self.handle.sendline( startCliCommand + str( ONOSIp ) )
-            i = self.handle.expect( [
-                self.karafPrompt,
-                pexpect.TIMEOUT ], onosStartTimeout )
+                startCliCommand = "onos " + str( ONOSIp )
+            self.handle.sendline( startCliCommand )
+            tries = 0
+            while tries < 5:
+                i = self.handle.expect( [
+                    self.karafPrompt,
+                    "Password:",
+                    pexpect.TIMEOUT ], onosStartTimeout )
 
-            if i == 0:
-                main.log.info( str( ONOSIp ) + " CLI Started successfully" )
-                if karafTimeout:  # FIXME: This doesn't look right
-                    self.handle.sendline(
-                        "config:property-set -p org.apache.karaf.shell\
-                                 sshIdleTimeout " +
-                        karafTimeout )
-                    self.handle.expect( self.prompt )
-                    self.handle.sendline( startCliCommand + str( ONOSIp ) )
-                    self.handle.expect( self.karafPrompt )
-                main.log.debug( self.handle.before )
-                return main.TRUE
-            else:
-                # If failed, send ctrl+c to process and try again
-                main.log.info( "Starting CLI failed. Retrying..." )
-                self.handle.send( "\x03" )
-                self.handle.sendline( startCliCommand + str( ONOSIp ) )
-                i = self.handle.expect( [ self.karafPrompt, pexpect.TIMEOUT ],
-                                        timeout=30 )
                 if i == 0:
-                    main.log.info( str( ONOSIp ) + " CLI Started " +
-                                   "successfully after retry attempt" )
+                    main.log.info( str( ONOSIp ) + " CLI Started successfully" )
                     if karafTimeout:
                         self.handle.sendline(
                             "config:property-set -p org.apache.karaf.shell\
-                                    sshIdleTimeout " +
+                                     sshIdleTimeout " +
                             karafTimeout )
-                        self.handle.expect( self.prompt )
-                        self.handle.sendline( startCliCommand + str( ONOSIp ) )
                         self.handle.expect( self.karafPrompt )
                     return main.TRUE
+                elif i == 1:
+                    main.log.info( str( ONOSIp ) + " CLI asking for password" )
+                    main.log.debug( "Sending %s" % self.karafPass )
+                    self.handle.sendline( self.karafPass )
                 else:
-                    main.log.error( "Connection to CLI " +
-                                    str( ONOSIp ) + " timeout" )
-                    return main.FALSE
-
+                    # If failed, send ctrl+c to process and try again
+                    main.log.info( "Starting CLI failed. Retrying..." )
+                    self.handle.send( "\x03" )
+                    self.handle.sendline( startCliCommand )
+                tries += 1
+            main.log.error( "Connection to CLI " + str( ONOSIp ) + " timeout" )
+            return main.FALSE
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
@@ -339,7 +342,7 @@
     def startCellCli( self, karafTimeout="",
                       commandlineTimeout=10, onosStartTimeout=60 ):
         """
-        Start CLI on onos ecll handle.
+        Start CLI on onos cell handle.
 
         karafTimeout is an optional argument. karafTimeout value passed
         by user would be used to set the current karaf shell idle timeout.
@@ -357,7 +360,7 @@
         try:
             self.handle.sendline( "" )
             x = self.handle.expect( [
-                self.prompt, self.karafPrompt ], commandlineTimeout )
+                self.Prompt(), self.karafPrompt ], commandlineTimeout )
 
             if x == 1:
                 main.log.info( "ONOS cli is already running" )
@@ -376,10 +379,9 @@
                         "config:property-set -p org.apache.karaf.shell\
                                  sshIdleTimeout " +
                         karafTimeout )
-                    self.handle.expect( self.prompt )
+                    self.handle.expect( self.Prompt() )
                     self.handle.sendline( "/opt/onos/bin/onos" )
                     self.handle.expect( self.karafPrompt )
-                main.log.debug( self.handle.before )
                 return main.TRUE
             else:
                 # If failed, send ctrl+c to process and try again
@@ -396,7 +398,7 @@
                             "config:property-set -p org.apache.karaf.shell\
                                     sshIdleTimeout " +
                             karafTimeout )
-                        self.handle.expect( self.prompt )
+                        self.handle.expect( self.Prompt() )
                         self.handle.sendline( "/opt/onos/bin/onos" )
                         self.handle.expect( self.karafPrompt )
                     return main.TRUE
@@ -476,7 +478,9 @@
         try:
             # Try to reconnect if disconnected from cli
             self.handle.sendline( "" )
-            i = self.handle.expect( [ self.karafPrompt, self.prompt, pexpect.TIMEOUT ] )
+            i = self.handle.expect( [ self.karafPrompt,
+                                      self.Prompt(),
+                                      pexpect.TIMEOUT ] )
             response = self.handle.before
             if i == 1:
                 main.log.error( self.name + ": onos cli session closed. " )
@@ -495,7 +499,7 @@
                     main.cleanAndExit()
             if i == 2:
                 main.log.warn( "Timeout when testing cli responsiveness" )
-                main.log.debug( self.handle.before )
+                main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
                 self.handle.send( "\x03" )  # Send ctrl-c to clear previous output
                 self.handle.expect( self.karafPrompt )
 
@@ -505,7 +509,7 @@
                 main.log.debug( self.name + ": " + repr( response ) )
         except pexpect.TIMEOUT:
             main.log.error( self.name + ": ONOS timeout" )
-            main.log.debug( self.handle.before )
+            main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
             self.handle.send( "\x03" )
             self.handle.expect( self.karafPrompt )
             return None
@@ -558,8 +562,9 @@
                 main.log.debug( self.name + ": Raw output" )
                 main.log.debug( self.name + ": " + repr( response ) )
 
+            response = self.cleanOutput( response, debug=debug )
             # Remove control codes from karaf 4.2.1
-            karafEscape = re.compile( r"('(0|1)~\'|\r\r\r\n\x1b\[A\x1b\[79C(x|\s)?|\x1b(>|=)|\x1b\[90m~)" )
+            karafEscape = re.compile( r"('(0|1)~\'|\r\r\r\n\x1b\[A\x1b\[79C(x|\s)?|\x1b(>|=~?)|\x1b\[90m~)" )
             response = karafEscape.sub( '', response )
             if debug:
                 main.log.debug( self.name + ": karafEscape output" )
@@ -570,11 +575,15 @@
             # Remove ANSI color control strings from output
             # NOTE: karaf is sometimes adding a single character then two
             #       backspaces and sometimes adding 2 characters with 2 backspaces??
-            backspaceEscape = re.compile( r'((..\x08\x08)|(.|\s)\x08)' )
-            response = backspaceEscape.sub( '', response )
-            if debug:
-                main.log.debug( self.name + ": backspaceEscape output" )
-                main.log.debug( self.name + ": " + repr( response ) )
+            backspaceEscape = re.compile( r'((.|\s)\x08)' )
+            unchanged = False
+            while not unchanged:
+                old = response
+                response = backspaceEscape.sub( '', response, count=1 )
+                if debug:
+                    main.log.debug( self.name + ": backspaceEscape output" )
+                    main.log.debug( self.name + ": " + repr( response ) )
+                unchanged = old == response
 
             # Remove extra return chars that get added
             response = re.sub(  r"\s\r", "", response )
@@ -632,7 +641,7 @@
         except pexpect.TIMEOUT:
             main.log.error( self.name + ": ONOS timeout" )
             if debug:
-                main.log.debug( self.handle.before )
+                main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
             self.exitFromCmd( self.karafPrompt, 100 )
             return None
         except IndexError:
@@ -6713,3 +6722,100 @@
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
+
+    def prepareForCLI( self, debug=True, maxRetries=120 ):
+        """
+        Prepare docker container to connect to onos cli
+        """
+        try:
+            # Wait for log files to be created
+            ready = 0
+            retries = 0
+            while not ready and retries < maxRetries:
+                retries += 1
+                self.handle.sendline( "ls -al /root/onos/apache-karaf-*/data/log" )
+                ready = self.handle.expect( [ "No such file or directory", self.dockerPrompt ] )
+                main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
+                if not ready:
+                    self.handle.expect( self.dockerPrompt )
+                time.sleep( 1 )
+            #main.log.debug( "%s: It took %s tries for onos log folder to %sbe created" %
+            #                    ( self.name, retries, "" if ready else "NOT " ) )
+
+            cmdList = []
+            cmdList.append( "apt-get update" )
+            cmdList.append( "apt-get install -y openssh-server" )
+            # Some built in scripts are hardcoded
+            cmdList.append( "ln -s /root/onos /opt/onos" )
+            cmdList.append( "ln -s /root/onos/apache-karaf-*/data/log /opt/onos/log" )
+            cmdList.append( "ls -al /opt/onos" )
+            output = ""
+            for cmdStr in cmdList:
+                self.handle.sendline( cmdStr )
+                self.handle.expect( self.dockerPrompt )
+                self.handle.sendline( "" )
+                self.handle.expect( self.dockerPrompt )
+                handle = self.handle.before
+                assert "command not found" not in handle, handle
+                assert "No such file or directory" not in handle, handle
+                output += handle
+            if debug:
+                main.log.debug( "%s: %s" % ( self.name, output ) )
+            return output
+        except AssertionError:
+            main.log.exception( "" )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
+
+    def onosSecureSSH( self, userName="onos", userPWD="rocks" ):
+        """
+        Enables secure access to ONOS console
+        by removing default users & keys.
+
+        bin/onos-user-password onos rocks
+
+        Returns: main.TRUE on success and main.FALSE on failure
+        """
+
+        try:
+            self.handle.sendline( "" )
+            self.handle.expect( self.dockerPrompt )
+
+            self.handle.sendline( "[ ! -f ~/.ssh/id_rsa.pub ] && ssh-keygen -t rsa -f ~/.ssh/id_rsa -P '' -q" )
+            self.handle.expect( self.dockerPrompt )
+            main.log.debug( "%s: %s%s" % ( self.name, self.handle.before, self.handle.after ) )
+
+            self.handle.sendline( "bin/onos-user-key $(id -un) $(cut -d\\\\ -f2 ~/.ssh/id_rsa.pub)" )
+            self.handle.expect( pexpect.TIMEOUT, timeout=10 )
+            main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
+
+            self.handle.sendline( "bin/onos-user-password %s %s" % ( userName, userPWD ) )
+            i = self.handle.expect( [ "usage",
+                                      self.dockerPrompt,
+                                      pexpect.TIMEOUT ] )
+            if i == 0:
+                # malformed command
+                main.log.warn( self.name + ": Could not parse onos-user-password command" )
+                self.handle.expect( self.dockerPrompt )
+                return main.FALSE
+            elif i == 1:
+                # Process started
+                main.log.info( self.name + ": SSH password added for user " + userName )
+                return main.TRUE
+            elif i == 2:
+                # timeout
+                main.log.error( self.name + ": Failed to secure onos ssh " )
+                main.log.debug( "%s: %s" % ( self.name, self.handle.before ) )
+        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/drivers/common/cli/onosclusterdriver.py b/TestON/drivers/common/cli/onosclusterdriver.py
index 83d4295..520250b 100755
--- a/TestON/drivers/common/cli/onosclusterdriver.py
+++ b/TestON/drivers/common/cli/onosclusterdriver.py
@@ -56,27 +56,19 @@
 
         We will look into each of the node's component handles to try to find the attreibute, looking at REST first
         """
-        usedDriver = False
         if hasattr( self.REST, name ):
-            if not usedDriver:
-                usedDriver = True
-                main.log.debug( "%s: Using Rest driver's attribute for '%s'" % ( self.name, name ) )
-                f = getattr( self.REST, name )
+            main.log.debug( "%s: Using Rest driver's attribute for '%s'" % ( self.name, name ) )
+            return getattr( self.REST, name )
         if hasattr( self.CLI, name ):
-            if not usedDriver:
-                usedDriver = True
-                main.log.debug( "%s: Using CLI driver's attribute for '%s'" % ( self.name, name ) )
-                f = getattr( self.CLI, name )
+            main.log.debug( "%s: Using CLI driver's attribute for '%s'" % ( self.name, name ) )
+            return getattr( self.CLI, name )
         if hasattr( self.Bench, name ):
-            if not usedDriver:
-                usedDriver = True
-                main.log.debug( "%s: Using Bench driver's attribute for '%s'" % ( self.name, name ) )
-                f = getattr( self.Bench, name )
-        if usedDriver:
-            return f
+            main.log.debug( "%s: Using Bench driver's attribute for '%s'" % ( self.name, name ) )
+            return getattr( self.Bench, name )
         raise AttributeError( "Could not find the attribute %s in %r or it's component handles" % ( name, self ) )
 
-    def __init__( self, name, ipAddress, CLI=None, REST=None, Bench=None, pos=None, userName=None, server=None ):
+    def __init__( self, name, ipAddress, CLI=None, REST=None, Bench=None, pos=None,
+                  userName=None, server=None, dockerPrompt=None ):
         # TODO: validate these arguments
         self.name = str( name )
         self.ipAddress = ipAddress
@@ -88,6 +80,7 @@
         self.ip_address = ipAddress
         self.user_name = userName
         self.server = server
+        self.dockerPrompt = dockerPrompt
 
 class OnosClusterDriver( CLI ):
 
@@ -98,6 +91,8 @@
         self.name = None
         self.home = None
         self.handle = None
+        self.useDocker = False
+        self.dockerPrompt = None
         self.nodes = []
         super( OnosClusterDriver, self ).__init__()
 
@@ -127,11 +122,17 @@
                     self.karafPass = self.options[ key ]
                 elif key == "cluster_name":
                     prefix = self.options[ key ]
+                elif key == "useDocker":
+                    self.useDocker = "True" == self.options[ key ]
+                elif key == "docker_prompt":
+                    self.dockerPrompt = self.options[ key ]
 
             self.home = self.checkOptions( self.home, "~/onos" )
             self.karafUser = self.checkOptions( self.karafUser, self.user_name )
             self.karafPass = self.checkOptions( self.karafPass, self.pwd )
             prefix = self.checkOptions( prefix, "ONOS" )
+            self.useDocker = self.checkOptions( self.useDocker, False )
+            self.dockerPrompt = self.checkOptions( self.dockerPrompt, "~/onos#" )
 
             self.name = self.options[ 'name' ]
 
@@ -435,4 +436,6 @@
             rest = self.createRestComponent( restName, ip )
             bench = self.createBenchComponent( benchName )
             server = self.createServerComponent( serverName, ip ) if createServer else None
-            self.nodes.append( Controller( prefix + str( i ), ip, cli, rest, bench, i - 1, self.user_name, server=server ) )
+            self.nodes.append( Controller( prefix + str( i ), ip, cli, rest, bench, i - 1,
+                                           self.user_name, server=server,
+                                           dockerPrompt=self.dockerPrompt ) )
diff --git a/TestON/drivers/common/cli/onosdriver.py b/TestON/drivers/common/cli/onosdriver.py
index a0a8dd1..6b13e3a 100755
--- a/TestON/drivers/common/cli/onosdriver.py
+++ b/TestON/drivers/common/cli/onosdriver.py
@@ -96,7 +96,7 @@
                             break
 
                     if not self.onosIps:
-                        main.log.info( "Could not read any environment variable"
+                        main.log.info( self.name + ": Could not read any environment variable"
                                        + " please load a cell file with all" +
                                         " onos IP" )
                         self.maxNodes = None
@@ -105,7 +105,7 @@
                                        str( self.onosIps.values() ) +
                                        " ONOS IPs" )
             except KeyError:
-                main.log.info( "Invalid environment variable" )
+                main.log.info( self.name + ": Invalid environment variable" )
             except Exception as inst:
                 main.log.error( "Uncaught exception: " + str( inst ) )
 
@@ -117,7 +117,7 @@
                                    ": Trying to connect to " +
                                    self.ip_address )
             except KeyError:
-                main.log.info( "Invalid host name," +
+                main.log.info( self.name + ": Invalid host name," +
                                " connecting to local host instead" )
                 self.ip_address = 'localhost'
             except Exception as inst:
@@ -136,7 +136,7 @@
                 self.handle.expect( self.prompt )
                 return self.handle
             else:
-                main.log.info( "Failed to create ONOS handle" )
+                main.log.info( self.name + ": Failed to create ONOS handle" )
                 return main.FALSE
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -224,7 +224,7 @@
                 elif i == 4:
                     # Prompt returned
                     break
-            main.log.info( "onos-package command returned: " + handle )
+            main.log.info( self.name + ": onos-package command returned: " + handle )
             # As long as the sendline does not time out,
             # return true. However, be careful to interpret
             # the results of the onos-package command return
@@ -258,7 +258,7 @@
             handle = str( self.handle.before )
             self.handle.expect( self.prompt )
 
-            main.log.info( "onos-build command returned: " +
+            main.log.info( self.name + ": onos-build command returned: " +
                            handle )
 
             if i == 0:
@@ -285,7 +285,7 @@
         On Failure, exits the test
         """
         try:
-            main.log.info( "Running 'mvn clean install' on " +
+            main.log.info( self.name + ": Running 'mvn clean install' on " +
                            str( self.name ) +
                            ". This may take some time." )
             self.handle.sendline( "cd " + self.home )
@@ -643,7 +643,7 @@
                     self.name +
                     ": Git Checkout %s : Already on this branch" % branch )
                 self.handle.expect( self.prompt )
-                # main.log.info( "DEBUG: after checkout cmd = "+
+                # main.log.info( self.name + ": DEBUG: after checkout cmd = "+
                 # self.handle.before )
                 return main.TRUE
             elif i == 3:
@@ -651,7 +651,7 @@
                     self.name +
                     ": Git checkout %s - Switched to this branch" % branch )
                 self.handle.expect( self.prompt )
-                # main.log.info( "DEBUG: after checkout cmd = "+
+                # main.log.info( self.name + ": DEBUG: after checkout cmd = "+
                 # self.handle.before )
                 return main.TRUE
             elif i == 4:
@@ -948,7 +948,7 @@
             self.handle.expect( self.prompt )
             handleBefore = self.handle.before
             handleAfter = self.handle.after
-            main.log.info( "Verify cell returned: " + handleBefore +
+            main.log.info( self.name + ": Verify cell returned: " + handleBefore +
                            handleAfter )
             return main.TRUE
         except pexpect.ExceptionPexpect:
@@ -989,7 +989,7 @@
             self.handle.expect( ":~" )
 
             if "value=" + paramValue + "," in self.handle.before:
-                main.log.info( "cfg " + configName + " successfully set to " + configParam )
+                main.log.info( self.name + ": cfg " + configName + " successfully set to " + configParam )
                 return main.TRUE
         except pexpect.ExceptionPexpect:
             main.log.exception( self.name + ": Pexpect exception found: " )
@@ -1030,13 +1030,19 @@
             self.handle.expect( self.prompt )
 
             self.handle.sendline( "onos-wait-for-start " + ONOSIp )
-            self.handle.expect( self.prompt )
+            i = self.handle.expect( [ self.prompt, "Password: " ] )
+            if i == 1:
+                self.handle.sendline( self.pwd )
+                self.handle.expect( self.prompt )
 
-            self.handle.sendline( "onos " + ONOSIp + " " + cmdstr )
-            i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=timeout )
+            self.handle.sendline( "ssh -q -p 8101 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null %s %s " % ( ONOSIp, cmdstr ) )
+            i = self.handle.expect( [ self.prompt, "Password: ", pexpect.TIMEOUT ], timeout=timeout )
+            if i == 1:
+                self.handle.sendline( self.pwd )
+                i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=timeout )
             if i == 0:
                 handleBefore = self.handle.before
-                main.log.info( "Command sent successfully" )
+                main.log.info( self.name + ": Command sent successfully" )
                 # Obtain return handle that consists of result from
                 # the onos command. The string may need to be
                 # configured further.
@@ -1044,6 +1050,7 @@
                 return returnString
             elif i == 1:
                 main.log.error( self.name + ": Timeout when sending " + cmdstr )
+                main.log.debug( self.handle.before )
                 self.handle.send( "\x03" )  # Control-C
                 self.handle.expect( self.prompt )
                 return main.FALSE
@@ -1140,7 +1147,7 @@
                 return main.TRUE
             elif i == 2:
                 # same bits are already on ONOS node
-                main.log.info( "ONOS is already installed on " + node )
+                main.log.info( self.name + ": ONOS is already installed on " + node )
                 self.handle.expect( self.prompt )
                 return main.TRUE
             elif i == 3:
@@ -1150,7 +1157,7 @@
                 return main.FALSE
             elif i == 4:
                 # prompt
-                main.log.info( "ONOS was installed on {} {}.".format(  node,
+                main.log.info( self.name + ": ONOS was installed on {} {}.".format(  node,
                                "but not started" if 'n' in options else "and started" ) )
                 return main.TRUE
             elif i == 5:
@@ -1190,14 +1197,14 @@
                 pexpect.TIMEOUT ], timeout=180 )
             if i == 0:
                 self.handle.expect( self.prompt )
-                main.log.info( "Service is already running" )
+                main.log.info( self.name + ": Service is already running" )
                 return main.TRUE
             elif i == 1:
                 self.handle.expect( self.prompt )
-                main.log.info( "ONOS service started" )
+                main.log.info( self.name + ": ONOS service started" )
                 return main.TRUE
             elif i == 2:
-                main.log.info( "ONOS service started" )
+                main.log.info( self.name + ": ONOS service started" )
                 return main.TRUE
             else:
                 self.handle.expect( self.prompt )
@@ -1230,11 +1237,11 @@
                 pexpect.TIMEOUT ], timeout=180 )
             if i == 0:
                 self.handle.expect( self.prompt )
-                main.log.info( "ONOS service stopped" )
+                main.log.info( self.name + ": ONOS service stopped" )
                 return main.TRUE
             elif i == 1:
                 self.handle.expect( self.prompt )
-                main.log.info( "onosStop() Unknown ONOS instance specified: " +
+                main.log.info( self.name + ": onosStop() Unknown ONOS instance specified: " +
                                str( nodeIp ) )
                 return main.FALSE
             elif i == 2:
@@ -1242,7 +1249,7 @@
                 main.log.warn( "ONOS wasn't running" )
                 return main.TRUE
             elif i == 3:
-                main.log.info( "ONOS service stopped" )
+                main.log.info( self.name + ": ONOS service stopped" )
                 return main.TRUE
             else:
                 main.log.error( "ONOS service failed to stop" )
@@ -1266,7 +1273,7 @@
             self.handle.expect( self.prompt, timeout=180 )
             self.handle.sendline( "onos-uninstall " + str( nodeIp ) )
             self.handle.expect( self.prompt, timeout=180 )
-            main.log.info( "ONOS " + nodeIp + " was uninstalled" )
+            main.log.info( self.name + ": ONOS " + nodeIp + " was uninstalled" )
             # onos-uninstall command does not return any text
             return main.TRUE
         except pexpect.TIMEOUT:
@@ -1297,13 +1304,13 @@
                 "ONOS\sprocess\sis\snot\srunning",
                 pexpect.TIMEOUT ], timeout=60 )
             if i == 0:
-                main.log.info( "ONOS instance " + str( nodeIp ) +
+                main.log.info( self.name + ": ONOS instance " + str( nodeIp ) +
                                " was killed and stopped" )
                 self.handle.sendline( "" )
                 self.handle.expect( self.prompt )
                 return main.TRUE
             elif i == 1:
-                main.log.info( "ONOS process was not running" )
+                main.log.info( self.name + ": ONOS process was not running" )
                 self.handle.sendline( "" )
                 self.handle.expect( self.prompt )
                 return main.FALSE
@@ -1337,7 +1344,7 @@
                         nodeIp ) + " was killed" )
                 return main.TRUE
             elif i == 1:
-                main.log.info( "No route to host" )
+                main.log.info( self.name + ": No route to host" )
                 return main.FALSE
             elif i == 2:
                 main.log.info(
@@ -1346,7 +1353,7 @@
                     " not configured" )
                 return main.FALSE
             else:
-                main.log.info( "ONOS instance was not killed" )
+                main.log.info( self.name + ": ONOS instance was not killed" )
                 return main.FALSE
 
         except pexpect.EOF:
@@ -1359,15 +1366,19 @@
 
     def onosAppInstall( self, nodeIp, oarFile ):
         """
-        Calls the command: 'onos-app nodeIp install! oarFile'
+        Calls the command: 'onos-app nodeIp reinstall! oarFile'
         Installs an ONOS application from an oar file
         """
         try:
-            cmd = "onos-app " + str( nodeIp ) +" install! " + str(oarFile)
+            cmd = "onos-app " + str( nodeIp ) + " reinstall! " + str( oarFile )
             self.handle.sendline( cmd )
-            self.handle.expect( self.prompt )
+            i = self.handle.expect( [ "409 Conflict", self.prompt ] )
+            if i == 0:
+                self.handle.expect( self.prompt )
+                time.sleep( 30 )
+                self.handle.sendline( cmd )
             handle = self.handle.before
-            main.log.debug( handle )
+            main.log.debug( "%s: %s" % ( self.name, handle ) )
             assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             assert "error" not in handle, handle
@@ -1442,7 +1453,7 @@
 
             self.handle.sendline( "onos-start-network " + mntopo )
             self.handle.expect( "mininet>" )
-            main.log.info( "Network started, entered mininet prompt" )
+            main.log.info( self.name + ": Network started, entered mininet prompt" )
 
             # TODO: Think about whether return is necessary or not
 
@@ -1557,7 +1568,7 @@
                 sendCmd = addApp + " > " + str( dirFile ) + " &"
             else:
                 sendCmd = addApp + " &"
-            main.log.info( "Send cmd: " + sendCmd )
+            main.log.info( self.name + ": Send cmd: " + sendCmd )
 
             self.handle.sendline( sendCmd )
 
@@ -1588,7 +1599,7 @@
             self.handle.sendline( "\n" )
             self.handle.expect( self.prompt )
 
-            main.log.info( "Tshark started capturing files on " +
+            main.log.info( self.name + ": Tshark started capturing files on " +
                            str( interface ) + " and saving to directory: " +
                            str( dirFile ) )
         except pexpect.EOF:
@@ -1687,7 +1698,7 @@
             self.handle.sendline( "sudo kill -9 `ps -ef | grep \"tshark -i\"" +
                                   " | grep -v grep | awk '{print $2}'`" )
             self.handle.sendline( "" )
-            main.log.info( "Tshark stopped" )
+            main.log.info( self.name + ": Tshark stopped" )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":    " + self.handle.before )
@@ -1713,7 +1724,7 @@
 
             if i == 0:
                 handle = self.handle.before
-                main.log.info( "ptpd returned an error: " +
+                main.log.info( self.name + ": ptpd returned an error: " +
                                str( handle ) )
                 return handle
             elif i == 1:
@@ -1856,10 +1867,10 @@
             self.handle.expect( self.prompt )
 
             if i == 0 or i == 1:
-                main.log.info( "ONOS is running" )
+                main.log.info( self.name + ": ONOS is running" )
                 return main.TRUE
             elif i == 2 or i == 3:
-                main.log.info( "ONOS is stopped" )
+                main.log.info( self.name + ": ONOS is stopped" )
                 main.log.error( "ONOS service failed to check the status" )
 
                 main.cleanAndExit()
@@ -2033,7 +2044,7 @@
 
                 deviceCount - number of switches to be assigned
         '''
-        main.log.info( "Creating link graph configuration file." )
+        main.log.info( self.name + ": Creating link graph configuration file." )
         linkGraphPath = self.home + "/tools/package/etc/linkGraph.cfg"
         tempFile = "/tmp/linkGraph.cfg"
 
@@ -2056,7 +2067,7 @@
                 switchList[ node ] += 1
 
         if isinstance( deviceCount, list ):
-            main.log.info( "Using provided device distribution" )
+            main.log.info( self.name + ": Using provided device distribution" )
             switchList = [ 0 ]
             for i in deviceCount:
                 switchList.append( int( i ) )
@@ -2099,7 +2110,7 @@
 
         # SCP
         os.system( "scp " + tempFile + " " + self.user_name + "@" + benchIp + ":" + linkGraphPath )
-        main.log.info( "linkGraph.cfg creation complete" )
+        main.log.info( self.name + ": linkGraph.cfg creation complete" )
 
     def configNullDev( self, ONOSIpList, deviceCount, numPorts=10 ):
         '''
@@ -2108,13 +2119,13 @@
             numPorts = number of ports per device. Defaults to 10 both in this function and in ONOS. Optional arg
         '''
 
-        main.log.info( "Configuring Null Device Provider" )
+        main.log.info( self.name + ": Configuring Null Device Provider" )
         clusterCount = len( ONOSIpList )
 
         try:
 
             if isinstance( deviceCount, int ) or isinstance( deviceCount, str ):
-                main.log.info( "Creating device distribution" )
+                main.log.info( self.name + ": Creating device distribution" )
                 deviceCount = int( deviceCount )
                 switchList = [ 0 ]*( clusterCount+1 )
                 baselineSwitchCount = deviceCount/clusterCount
@@ -2126,7 +2137,7 @@
                     switchList[ node ] += 1
 
             if isinstance( deviceCount, list ):
-                main.log.info( "Using provided device distribution" )
+                main.log.info( self.name + ": Using provided device distribution" )
 
                 if len( deviceCount ) == clusterCount:
                     switchList = [ '0' ]
@@ -2207,7 +2218,7 @@
             main.log.error( self.name + ":    " + self.handle.before )
             main.cleanAndExit()
         except AssertionError:
-            main.log.info( "Settings did not post to ONOS" )
+            main.log.info( self.name + ": Settings did not post to ONOS" )
             main.log.error( verification )
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
@@ -2270,7 +2281,7 @@
                         the end point for extraction of data
         """
         try:
-            main.log.info( " Log Report for {} ".format( nodeIp ).center( 70, '=' ) )
+            main.log.info( self.name + ":  Log Report for {} ".format( nodeIp ).center( 70, '=' ) )
             if isinstance( searchTerms, str ):
                 searchTerms = [ searchTerms ]
             numTerms = len( searchTerms )
@@ -2298,7 +2309,7 @@
                         count += 1
                         if before.index( line ) > ( len( before ) - 7 ):
                             logLines[ termIndex ].append( line )
-                main.log.info( "{}: {}".format( term, count ) )
+                main.log.info( self.name + ": {}: {}".format( term, count ) )
                 totalHits += count
                 if termIndex == numTerms - 1:
                     print "\n"
@@ -2310,7 +2321,7 @@
                         outputString += ( "\t" + term[ line ] + "\n" )
                     if outputString != ( term[ 0 ] + ": \n" ):
                         main.log.info( outputString )
-            main.log.info( "=" * 70 )
+            main.log.info( self.name + ": =" * 70 )
             return totalHits
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2551,7 +2562,7 @@
 
         return main.TRUE if onosStatus else main.FALSE
 
-    def onosNetCfg( self, controllerIp, path, fileName ):
+    def onosNetCfg( self, controllerIp, path, fileName, user=None, password=None):
         """
         Push a specified json file to ONOS through the onos-netcfg service
 
@@ -2564,14 +2575,23 @@
         there is an error.
         """
         try:
-            cmd = "onos-netcfg {0} {1}{2}".format( controllerIp, path, fileName )
-            main.log.info( "Sending: " + cmd )
-            main.ONOSbench.handle.sendline( cmd )
-            main.ONOSbench.handle.expect( self.prompt )
+            cmd = "onos-netcfg "
+            if user:
+                cmd += "-u %s " % user
+            if password:
+                cmd += "-p %s " % password
+            cmd += "{0} {1}{2}".format( controllerIp, path, fileName )
+            main.log.info( self.name + ": Sending: " + cmd )
+            self.handle.sendline( cmd )
+            self.handle.expect( self.prompt )
             handle = self.handle.before
-            if "Error" in handle or "No such file or directory" in handle or "curl: " in handle:
+            if "Error" in handle or\
+               "No such file or directory" in handle or\
+               "command not found" in handle or\
+               "curl: " in handle:
                 main.log.error( self.name + ":    " + handle + self.handle.after )
                 return main.FALSE
+            main.log.debug( self.name + ":    " + handle )
             return main.TRUE
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2588,7 +2608,7 @@
         try:
             onosIPs = " ".join( onosIPs )
             command = "onos-form-cluster {}".format(  onosIPs )
-            main.log.info( "Sending: " + command )
+            main.log.info( self.name + ": Sending: " + command )
             self.handle.sendline( "" )
             self.handle.expect( self.prompt )
             self.handle.sendline( command )
@@ -2787,16 +2807,16 @@
                 pexpect.TIMEOUT ], timeout=60 )
 
             if i == 0:
-                main.log.info( "Atomix instance " + str( nodeIp ) + " was killed" )
+                main.log.info( self.name + ": Atomix instance " + str( nodeIp ) + " was killed" )
                 return main.TRUE
             elif i == 1:
-                main.log.info( "No route to host" )
+                main.log.info( self.name + ": No route to host" )
                 return main.FALSE
             elif i == 2:
-                main.log.info( "Passwordless login for host: " + str( nodeIp ) + " not configured" )
+                main.log.info( self.name + ": Passwordless login for host: " + str( nodeIp ) + " not configured" )
                 return main.FALSE
             else:
-                main.log.info( "Atomix instance was not killed" )
+                main.log.info( self.name + ": Atomix instance was not killed" )
                 return main.FALSE
 
         except pexpect.EOF:
@@ -2817,7 +2837,7 @@
             self.handle.expect( self.prompt, timeout=180 )
             self.handle.sendline( "atomix-uninstall " + str( nodeIp ) )
             self.handle.expect( self.prompt, timeout=180 )
-            main.log.info( "Atomix " + nodeIp + " was uninstalled" )
+            main.log.info( self.name + ": Atomix " + nodeIp + " was uninstalled" )
             # onos-uninstall command does not return any text
             return main.TRUE
         except pexpect.TIMEOUT:
@@ -2913,9 +2933,9 @@
             if dstPath:
                 cmd += "-P %s " % ( dstPath )
             cmd += str( url )
-            main.log.info( "Sending: " + cmd )
-            main.ONOSbench.handle.sendline( cmd )
-            main.ONOSbench.handle.expect( self.prompt )
+            main.log.info( self.name + ": Sending: " + cmd )
+            self.handle.sendline( cmd )
+            self.handle.expect( self.prompt )
             output = self.handle.before
             main.log.debug( output )
             if "Error" in output or "No such file or directory" in output:
@@ -2948,9 +2968,9 @@
         # FIXME: Not all options may work, more testing is required, only tested with install(!)
         try:
             cmd = "onos-app %s %s %s/%s" % ( onosIP, option, filePath, fileName )
-            main.log.info( "Sending: " + cmd )
-            main.ONOSbench.handle.sendline( cmd )
-            main.ONOSbench.handle.expect( self.prompt )
+            main.log.info( self.name + ": Sending: " + cmd )
+            self.handle.sendline( cmd )
+            self.handle.expect( self.prompt )
             handle = self.handle.before
             main.log.debug( handle )
             if "Error" in handle or "usage: " in handle or "curl: " in handle:
@@ -2964,3 +2984,101 @@
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
+
+    def makeDocker( self, path, cmd, prompt="Successfully tagged", timeout=600 ):
+        """
+        Build a docker image using a command, such as make
+        Arguments:
+            - path: a path where the script is located. will cd to path
+            - cmd: the command to run
+        Optional Arguments:
+            - prompt: A custom prompt to expect after the command is finished,
+                      incase the host prompt is printed during the build
+            - timeout: how long to wait for the build
+        """
+        try:
+            main.log.warn( "%s: makeDocker()" % self.name )
+            self.handle.sendline( "cd %s" % path )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( cmd )
+            self.handle.expect( prompt, timeout=timeout )
+            fullResponse = self.handle.before
+            tailResponse = self.handle.after
+            # TODO: error checking, might be difficult with custom expects
+            self.handle.expect( self.prompt )
+            tailResponse += self.handle.before + self.handle.after
+            fullResponse += tailResponse
+            main.log.debug( self.name + ": " + tailResponse )
+            self.handle.sendline( "cd %s" % self.home )
+            self.handle.expect( self.prompt )
+            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.log.debug( self.name + ":    " + self.handle.before )
+            main.cleanAndExit()
+
+    def generateOnosConfig( self, nodeIp, path="cluster.json" ):
+        """
+        Generate onos cluster configuration file
+        Arguments:
+         - nodeIp: ip of the node this file is fore
+        Optional Arguments:
+         - The path to save the file to
+        """
+        try:
+            main.log.info( "%s: Generating onos config file for %s" % ( self.name, nodeIp ) )
+            self.handle.sendline( "onos-gen-config %s %s" % ( nodeIp, path ) )
+            i = self.handle.expect( [ self.prompt, "not found", "Error" ] )
+            response = self.handle.before
+            if i == 0:
+                main.log.debug( "%s: %s" % ( self.name, response ) )
+                return main.TRUE
+            else:
+                response += self.handle.after
+                self.handle.expect( self.prompt )
+                response += self.handle.before
+                main.log.debug( "%s: %s" % ( self.name, response ) )
+                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.log.debug( self.name + ":    " + self.handle.before )
+            main.cleanAndExit()
+
+    def generateAtomixConfig( self, nodeIp, path="atomix.json" ):
+        """
+        Generate atomix cluster configuration file
+        Arguments:
+         - nodeIp: ip of the node this file is fore
+        Optional Arguments:
+         - The path to save the file to
+        """
+        try:
+            main.log.info( "%s: Generating atomix config file for %s" % ( self.name, nodeIp ) )
+            self.handle.sendline( "atomix-gen-config %s %s" % ( nodeIp, path ) )
+            i = self.handle.expect( [ self.prompt, "not found", "Error" ] )
+            response = self.handle.before
+            if i == 0:
+                main.log.debug( "%s: %s" % ( self.name, response ) )
+                return main.TRUE
+            else:
+                response += self.handle.after
+                self.handle.expect( self.prompt )
+                response += self.handle.before
+                main.log.debug( "%s: %s" % ( self.name, response ) )
+                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.log.debug( self.name + ":    " + self.handle.before )
+            main.cleanAndExit()
diff --git a/TestON/drivers/common/clidriver.py b/TestON/drivers/common/clidriver.py
index 2db3914..928db71 100644
--- a/TestON/drivers/common/clidriver.py
+++ b/TestON/drivers/common/clidriver.py
@@ -33,6 +33,7 @@
     """
     def __init__( self ):
         super( CLI, self ).__init__()
+        self.inDocker = False
 
     def checkPrompt( self ):
         for key in self.options:
@@ -92,7 +93,7 @@
                                       self.prompt ],
                                     120 )
             if i == 0:  # Accept key, then expect either a password prompt or access
-                main.log.info( "ssh key confirmation received, send yes" )
+                main.log.info( self.name + ": ssh key confirmation received, send yes" )
                 self.handle.sendline( 'yes' )
                 i = 5  # Run the loop again
                 continue
@@ -101,7 +102,7 @@
                     main.log.info(
                             "ssh connection asked for password, gave password" )
                 else:
-                    main.log.info( "Server asked for password, but none was "
+                    main.log.info( self.name + ": Server asked for password, but none was "
                                     "given in the .topo file. Trying "
                                     "no password." )
                     self.pwd = ""
@@ -114,10 +115,10 @@
                                         pexpect.TIMEOUT ],
                                         120 )
                 if j != 2:
-                    main.log.error( "Incorrect Password" )
+                    main.log.error( self.name + ": Incorrect Password" )
                     return main.FALSE
             elif i == 2:
-                main.log.error( "Connection timeout" )
+                main.log.error( self.name + ": Connection timeout" )
                 return main.FALSE
             elif i == 3:  # timeout
                 main.log.error(
@@ -133,10 +134,10 @@
                     " port 22: Connection refused" )
                 return main.FALSE
             elif i == 6:  # Incorrect Password
-                main.log.error( "Incorrect Password" )
+                main.log.error( self.name + ": Incorrect Password" )
                 return main.FALSE
             elif i == 7:  # Prompt
-                main.log.info( "Password not required logged in" )
+                main.log.info( self.name + ": Password not required logged in" )
 
         self.handle.sendline( "" )
         self.handle.expect( self.prompt )
@@ -147,7 +148,12 @@
     def disconnect( self ):
         result = super( CLI, self ).disconnect( self )
         result = main.TRUE
-        # self.execute( cmd="exit",timeout=120,prompt="(.*)" )
+
+    def Prompt( self ):
+        """
+        Returns the prompt to expect depending on what program we are in
+        """
+        return self.prompt if not self.inDocker else self.dockerPrompt
 
     def execute( self, **execparams ):
         """
@@ -191,7 +197,7 @@
             self.LASTRSP = self.LASTRSP + \
                 self.handle.before + self.handle.after
             if not args[ "LOGCMD" ] is False:
-                main.log.info( "Executed :" + str( cmd ) +
+                main.log.info( self.name + ": Executed :" + str( cmd ) +
                                " \t\t Expected Prompt '" + str( expectPrompt ) +
                                "' Found" )
         elif index == 1:
@@ -209,10 +215,10 @@
                     [ "--More--", expectPrompt ], timeout=timeoutVar )
                 self.LASTRSP = self.LASTRSP + self.handle.before
         elif index == 2:
-            main.log.error( "Command not found" )
+            main.log.error( self.name + ": Command not found" )
             self.LASTRSP = self.LASTRSP + self.handle.before
         elif index == 3:
-            main.log.error( "Expected Prompt not found, Time Out!!" )
+            main.log.error( self.name + ": Expected Prompt not found, Time Out!!" )
             main.log.error( expectPrompt )
             self.LASTRSP = self.LASTRSP + self.handle.before
             return self.LASTRSP
@@ -253,7 +259,7 @@
             handle.expect( default )
 
         if i == 2:
-            main.log.error( "Unable to run as Sudo user" )
+            main.log.error( self.name + ": Unable to run as Sudo user" )
 
         return handle
 
@@ -299,7 +305,7 @@
             main.log.debug( "Wrong direction using secure copy command!" )
             return main.FALSE
 
-        main.log.info( "Sending: " + cmd )
+        main.log.info( self.name + ": Sending: " + cmd )
         self.handle.sendline( cmd )
         i = 0
         while i < 2:
@@ -315,13 +321,13 @@
                                 pexpect.TIMEOUT ],
                                 120 )
             if i == 0:  # ask for ssh key confirmation
-                main.log.info( "ssh key confirmation received, sending yes" )
+                main.log.info( self.name + ": ssh key confirmation received, sending yes" )
                 self.handle.sendline( 'yes' )
             elif i == 1:  # Asked for ssh password
-                main.log.info( "ssh connection asked for password, gave password" )
+                main.log.info( self.name + ": ssh connection asked for password, gave password" )
                 self.handle.sendline( pwd )
             elif i == 2:  # File finished transfering
-                main.log.info( "Secure copy successful" )
+                main.log.info( self.name + ": Secure copy successful" )
                 returnVal = main.TRUE
             elif i == 3:  # Connection refused
                 main.log.error(
@@ -330,15 +336,15 @@
                     " port 22: Connection refused" )
                 returnVal = main.FALSE
             elif i == 4:  # File Not found
-                main.log.error( "No such file found" )
+                main.log.error( self.name + ": No such file found" )
                 returnVal = main.FALSE
             elif i == 5:  # Permission denied
-                main.log.error( "Permission denied. Check folder permissions" )
+                main.log.error( self.name + ": Permission denied. Check folder permissions" )
                 returnVal = main.FALSE
             elif i == 6:  # prompt returned
                 return returnVal
             elif i == 7:  # EOF
-                main.log.error( "Pexpect.EOF found!!!" )
+                main.log.error( self.name + ": Pexpect.EOF found!!!" )
                 main.cleanAndExit()
             elif i == 8:  # timeout
                 main.log.error(
@@ -396,7 +402,7 @@
                                  self.prompt ],
                                120 )
             if i == 0:  # Accept key, then expect either a password prompt or access
-                main.log.info( "ssh key confirmation received, send yes" )
+                main.log.info( self.name + ": ssh key confirmation received, send yes" )
                 handle.sendline( 'yes' )
                 i = 5  # Run the loop again
                 continue
@@ -405,7 +411,7 @@
                     main.log.info(
                             "ssh connection asked for password, gave password" )
                 else:
-                    main.log.info( "Server asked for password, but none was "
+                    main.log.info( self.name + ": Server asked for password, but none was "
                                     "given in the .topo file. Trying "
                                     "no password." )
                     pwd = ""
@@ -416,10 +422,10 @@
                                      pexpect.TIMEOUT ],
                                      120 )
                 if j != 0:
-                    main.log.error( "Incorrect Password" )
+                    main.log.error( self.name + ": Incorrect Password" )
                     main.cleanAndExit()
             elif i == 2:
-                main.log.error( "Connection timeout" )
+                main.log.error( self.name + ": Connection timeout" )
                 main.cleanAndExit()
             elif i == 3:  # timeout
                 main.log.error(
@@ -435,30 +441,30 @@
                     " port 22: Connection refused" )
                 main.cleanAndExit()
             elif i == 6:
-                main.log.info( "Password not required logged in" )
+                main.log.info( self.name + ": Password not required logged in" )
 
         handle.sendline( "" )
         handle.expect( self.prompt )
         handle.sendline( "cd" )
         handle.expect( self.prompt )
 
-        main.log.info( "Successfully ssh to " + ipAddress + "." )
+        main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
         return handle
 
     def exitFromSsh( self, handle, ipAddress ):
         try:
             handle.sendline( "logout" )
             handle.expect( "closed." )
-            main.log.info( "Successfully closed ssh connection from " + ipAddress )
+            main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
         except pexpect.EOF:
-            main.log.error( "Failed to close the connection from " + ipAddress )
+            main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
         try:
             # check that this component handle still works
             self.handle.sendline( "" )
             self.handle.expect( self.prompt )
         except pexpect.EOF:
             main.log.error( self.handle.before )
-            main.log.error( "EOF after closing ssh connection" )
+            main.log.error( self.name + ": EOF after closing ssh connection" )
 
     def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
         """
@@ -548,7 +554,8 @@
                 cmd = "unset {}".format( variable )
             self.handle.sendline( cmd )
             self.handle.expect( self.prompt )
-            main.log.debug( self.handle.before )
+            output = self.handle.before
+            main.log.debug( output )
             return True
         except AssertionError:
             main.log.error( self.name + ": Could not execute command: " + output )
@@ -604,3 +611,315 @@
             main.log.debug( self.name + ": cleanOutput:" )
             main.log.debug( self.name + ": " + repr( cleaned ) )
         return cleaned
+
+    def dockerPull( self, image, tag=None ):
+        """
+        Pull a docker image from a registry
+        """
+        try:
+            imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
+            cmdStr = "docker pull %s" % imgStr
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.prompt,
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ], 120 )
+            if i == 0:
+                return main.TRUE
+            else:
+                main.log.error( self.name + ": Error pulling docker image " + imgStr  )
+                output = self.handle.before + str( self.handle.after )
+                if i == 1:
+                    self.handle.expect( self.prompt )
+                    output += self.handle.before + str( self.handle.after )
+                main.log.debug( self.name + ": " + output )
+                return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
+        """
+        Build a docker image
+        Required Arguments:
+         - path: Path to the dockerfile, it is recommended to avoid relative paths
+         - imageTag: Give a tag to the built docker image
+        Optional Arguments:
+         - pull: Whether to attempt to pull latest images before building
+         - options: A string containing any addition optional arguments
+                    for the docker build command
+         - timeout: How many seconds to wait for the build to complete
+        """
+        try:
+            response = main.TRUE
+            if pull:
+                options = "--pull " + options
+            cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ "Successfully built",
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ], timeout=timeout )
+            output = self.handle.before
+            if i == 0:
+                output += self.handle.after
+                self.handle.expect( self.prompt )
+                output += self.handle.before + self.handle.after
+                return response
+            elif i == 1:
+                response = main.FALSE
+                output += self.handle.after
+                self.handle.expect( self.prompt )
+                output += self.handle.before + self.handle.after
+            elif i == 2:
+                response = main.FALSE
+            main.log.error( self.name + ": Error building docker image" )
+            main.log.debug( self.name + ": " + output )
+            return response
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerStop( self, containerName ):
+        """
+        Stop a docker container
+        Required Arguments:
+         - containerName: Name of the container to stop
+        """
+        try:
+            cmdStr = "docker stop %s" % ( containerName )
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.prompt,
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ], 120 )
+            output = self.handle.before
+            if i == 0:
+                return main.TRUE
+            elif i == 1:
+                output += self.handle.after
+                self.handle.expect( self.prompt )
+                output += self.handle.before
+            elif i == 2:
+                pass
+            main.log.debug( "%s: %s" % ( self.name, output ) )
+            if "No such container" in output:
+                return main.TRUE
+            main.log.error( self.name + ": Error stopping docker image" )
+            main.log.debug( self.name + ": " + output )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerRun( self, image, containerName, options="", imageArgs="" ):
+        """
+        Run a docker image
+        Required Arguments:
+         - containerName: Give a name to the container once its started
+         - image: Run the given image
+        Optional Arguments:
+         - options: A string containing any addition optional arguments
+                    for the docker run command
+        - imageArgs: A string containing command line arguments for the
+                     command run by docker
+        """
+        try:
+            cmdStr = "docker run --name %s %s %s %s" % ( containerName,
+                                                         options if options else "",
+                                                         image,
+                                                         imageArgs )
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.prompt,
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ], 120 )
+            if i == 0:
+                return main.TRUE
+            else:
+                output = self.handle.before
+                main.log.debug( self.name + ": " + output )
+                main.log.error( self.name + ": Error running docker image" )
+                if i == 1:
+                    output += self.handle.after
+                    self.handle.expect( self.prompt )
+                    output += self.handle.before + self.handle.after
+                main.log.debug( self.name + ": " + output )
+                return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerAttach( self, containerName, dockerPrompt="" ):
+        """
+        Attach to a docker image
+        Required Arguments:
+         - containerName: The name of the container to attach to
+        Optional Arguments:
+         - dockerPrompt: a regex for matching the docker shell prompt
+        """
+        try:
+            if dockerPrompt:
+                self.dockerPrompt = dockerPrompt
+            cmdStr = "docker attach %s" % containerName
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.dockerPrompt,
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ] )
+            if i == 0:
+                self.inDocker = True
+                return main.TRUE
+            else:
+                main.log.error( self.name + ": Error connecting to docker container" )
+                output = self.handle.before + str( self.handle.after )
+                if i == 1:
+                    self.handle.expect( self.prompt )
+                    output += self.handle.before + str( self.handle.after )
+                main.log.debug( self.name + ": " + output )
+                return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except AttributeError as e:
+            main.log.exception( self.name + ": AttributeError - " + str( e ) )
+            main.log.warn( self.name + ": Make sure dockerPrompt is set" )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
+        """
+        Attach to a docker image
+        Required Arguments:
+         - containerName: The name of the container to attach to
+        Optional Arguments:
+         - command: Command to run in the docker container
+         - options: Docker exec options
+         - dockerPrompt: a regex for matching the docker shell prompt
+        """
+        try:
+            if dockerPrompt:
+                self.dockerPrompt = dockerPrompt
+            cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.dockerPrompt,
+                                      "Error response from daemon",
+                                      pexpect.TIMEOUT ] )
+            if i == 0:
+                self.inDocker = True
+                return main.TRUE
+            else:
+                main.log.error( self.name + ": Error connecting to docker container" )
+                output = self.handle.before + str( self.handle.after )
+                if i == 1:
+                    self.handle.expect( self.prompt )
+                    output += self.handle.before + str( self.handle.after )
+                main.log.debug( self.name + ": " + output )
+                return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except AttributeError as e:
+            main.log.exception( self.name + ": AttributeError - " + str( e ) )
+            main.log.warn( self.name + ": Make sure dockerPrompt is set" )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
+        """
+        Copy a file from/to a docker container to the host
+        Required Arguments:
+         - containerName: The name of the container to copy from/to
+         - dockerPath: the path in the container to copy from/to
+         - hostPath: the path on the host to copy to/from
+        Optional Arguments:
+         - direction: Choose whether to copy "from" the container or "to" the container
+        """
+        try:
+            cmdStr = "docker cp "
+            if direction == "from":
+                cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
+            elif direction == "to":
+                cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
+            main.log.info( self.name + ": sending: " + cmdStr )
+            self.handle.sendline( cmdStr)
+            i = self.handle.expect( [ self.prompt,
+                                      "Error",
+                                      pexpect.TIMEOUT ] )
+            if i == 0:
+                retValue = main.TRUE
+            else:
+                main.log.error( self.name + ": Error in docker cp" )
+                output = self.handle.before + str( self.handle.after )
+                if i == 1:
+                    self.handle.expect( self.prompt )
+                    output += self.handle.before + str( self.handle.after )
+                main.log.debug( self.name + ": " + output )
+                retValue = main.FALSE
+            return retValue
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except AttributeError as e:
+            main.log.exception( self.name + ": AttributeError - " + str( e ) )
+            main.log.warn( self.name + ": Make sure dockerPrompt is set" )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
+
+    def dockerDisconnect( self ):
+        """
+        Send ctrl-c, ctrl-d to session, which should close and exit the
+        attached docker session. This will likely exit the running program
+        in the container and also stop the container.
+        """
+        try:
+            cmdStr = "\x03"
+            main.log.info( self.name + ": sending: " + repr( cmdStr ) )
+            self.handle.send( cmdStr)
+            cmdStr = "\x04"
+            main.log.info( self.name + ": sending: " + repr( cmdStr ) )
+            self.handle.send( cmdStr)
+            i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
+            if i == 0:
+                self.inDocker = False
+                return main.TRUE
+            else:
+                main.log.error( self.name + ": Error disconnecting from docker image" )
+                main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
+                return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":     " + self.handle.before )
+            return main.FALSE
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            return main.FALSE
diff --git a/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py
index ed7bf61..85dcfc2 100644
--- a/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py
+++ b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py
@@ -147,7 +147,7 @@
         main.step( "Executing onos-form-cluster" )
         formClusterResult = main.ONOSbench.formCluster( main.Cluster.getIps( True, True ) )
         utilities.assert_equals( expect=main.TRUE,
-                                 actual=result,
+                                 actual=formClusterResult,
                                  onpass="Successfully formed clusters to ONOS",
                                  onfail="Failed to form clusters to ONOS" )
         onosServiceResult = main.testSetUp.checkOnosService( main.Cluster )
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
index 9219001..9b1a5c9 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params.stratum
@@ -23,8 +23,6 @@
         <topology>trellis_fabric.py</topology>
         <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
-        <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
-        <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
     </DEPENDENCY>
 
     <MN_DOCKER>
@@ -33,9 +31,21 @@
         <home>/home/root/</home>
     </MN_DOCKER>
 
+    <CLUSTER>
+        # Params for onos docker
+        <dockerSkipBuild>True</dockerSkipBuild>
+        <dockerBuildCmd>make ONOS_BRANCH=master DOCKER_TAG=TestON-master onos-build trellis-control-build trellis-t3-build tost-build</dockerBuildCmd> # If using another cmd like make
+        <dockerBuildTimeout>1200</dockerBuildTimeout>
+        <dockerFilePath>~/tost-onos</dockerFilePath>
+        <dockerImageTag>tost:TestON-master</dockerImageTag>
+        <dockerOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/cluster.json:/root/onos/config/cluster.json </dockerOptions> # We start the container detached, so the docker component can connect to cli instead of logs
+        <atomixImageTag>atomix/atomix:3.1.5</atomixImageTag>
+        <atomixOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/atomix.json:/opt/atomix/conf/atomix.json </atomixOptions>
+    </CLUSTER>
+
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,fpm,netcfghostprovider,drivers.bmv2,pipelines.fabric</cellApps>
+        <cellApps>drivers,fpm,netcfghostprovider,drivers.bmv2,pipelines.fabric,segmentrouting,t3</cellApps>
     </ENV>
 
     <ONOS_Configuration>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo
index 82c7b3a..78792d0 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo
@@ -8,6 +8,8 @@
             <connect_order>1</connect_order>
             <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
             <COMPONENTS>
+                <useDocker></useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
                 <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></karaf_username>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.0x1.physical b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.0x1.physical
index d103eae..d6828b3 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.0x1.physical
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.0x1.physical
@@ -8,12 +8,14 @@
             <connect_order>1</connect_order>
             <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
             <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
                 <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.
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <web_user></web_user>
-                <web_pass></web_pass>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.physical b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.physical
index 905d24b..37e1830 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.physical
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.topo.physical
@@ -8,6 +8,8 @@
             <connect_order>1</connect_order>
             <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
             <COMPONENTS>
+                <useDocker></useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
                 <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></karaf_username>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRClusterRestart/SRClusterRestart.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRClusterRestart/SRClusterRestart.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRClusterRestart/SRClusterRestart.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
index 6f12598..e7e1564 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params.stratum
@@ -17,12 +17,11 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>True</useBmv2>
         <bmv2SwitchType>stratum</bmv2SwitchType>
+        <switchPrefix>bmv2</switchPrefix>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>trellis_fabric.py</topology>
         <lib>routinglib.py,trellislib.py,stratum.py</lib>
         <conf>dhcpd.conf,dhcpd6.conf,bgpdr1.conf,bgpdr2.conf,bgpdbgp1.conf,zebradbgp1.conf,bgpdbgp2.conf,zebradbgp2.conf</conf>
-        <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
-        <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
     </DEPENDENCY>
 
     <MN_DOCKER>
@@ -31,9 +30,21 @@
         <home>/home/root/</home>
     </MN_DOCKER>
 
+    <CLUSTER>
+        # Params for onos docker
+        <dockerSkipBuild>True</dockerSkipBuild>
+        <dockerBuildCmd>make ONOS_BRANCH=master DOCKER_TAG=TestON-master onos-build trellis-control-build trellis-t3-build tost-build</dockerBuildCmd> # If using another cmd like make
+        <dockerBuildTimeout>1200</dockerBuildTimeout>
+        <dockerFilePath>~/tost-onos</dockerFilePath>
+        <dockerImageTag>tost:TestON-master</dockerImageTag>
+        <dockerOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/cluster.json:/root/onos/config/cluster.json </dockerOptions> # We start the container detached, so the docker component can connect to cli instead of logs
+        <atomixImageTag>atomix/atomix:3.1.5</atomixImageTag>
+        <atomixOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/atomix.json:/opt/atomix/conf/atomix.json </atomixOptions>
+    </CLUSTER>
+
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,fpm,dhcprelay,netcfghostprovider,routeradvertisement,drivers.bmv2,pipelines.fabric</cellApps>
+        <cellApps>drivers,fpm,dhcprelay,netcfghostprovider,routeradvertisement,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDynamic/SRDynamic.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRDynamic/SRDynamic.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRDynamic/SRDynamic.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRHighAvailability/SRHighAvailability.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRHighAvailability/SRHighAvailability.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRHighAvailability/SRHighAvailability.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRLinkFailure/SRLinkFailure.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRLinkFailure/SRLinkFailure.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRLinkFailure/SRLinkFailure.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SROnosFailure/SROnosFailure.topo.docker b/TestON/tests/USECASE/SegmentRouting/SROnosFailure/SROnosFailure.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SROnosFailure/SROnosFailure.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
index 38edc6f..835baff 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params.stratum
@@ -17,12 +17,11 @@
         <useCommonTopo>True</useCommonTopo>
         <useBmv2>True</useBmv2>
         <bmv2SwitchType>stratum</bmv2SwitchType>
+        <switchPrefix>bmv2</switchPrefix>
         <stratumRoot>~/stratum</stratumRoot>
         <topology>hagg_fabric.py</topology>
         <lib>routinglib.py,trellislib.py,trellis_fabric.py,stratum.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
-        <trellisOar>/home/sdn/segmentrouting-oar-3.0.0-SNAPSHOT.oar</trellisOar>
-        <t3Oar>/home/sdn/t3-app-3.0.0-SNAPSHOT.oar</t3Oar>
     </DEPENDENCY>
 
     <MN_DOCKER>
@@ -31,9 +30,21 @@
         <home>/home/root/</home>
     </MN_DOCKER>
 
+    <CLUSTER>
+        # Params for onos docker
+        <dockerSkipBuild>True</dockerSkipBuild>
+        <dockerBuildCmd>make ONOS_BRANCH=master DOCKER_TAG=TestON-master onos-build trellis-control-build trellis-t3-build tost-build</dockerBuildCmd> # If using another cmd like make
+        <dockerBuildTimeout>1200</dockerBuildTimeout>
+        <dockerFilePath>~/tost-onos</dockerFilePath>
+        <dockerImageTag>tost:TestON-master</dockerImageTag>
+        <dockerOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/cluster.json:/root/onos/config/cluster.json </dockerOptions> # We start the container detached, so the docker component can connect to cli instead of logs
+        <atomixImageTag>atomix/atomix:3.1.5</atomixImageTag>
+        <atomixOptions>-d --rm --network host -v ~/.ssh/authorized_keys:/root/.ssh/authorized_keys -v /tmp/atomix.json:/opt/atomix/conf/atomix.json </atomixOptions>
+    </CLUSTER>
+
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,fpm,dhcprelay,netcfghostprovider,routeradvertisement,hostprobingprovider,drivers.bmv2,pipelines.fabric</cellApps>
+        <cellApps>drivers,fpm,dhcprelay,hostprovider,netcfghostprovider,lldpprovider,routeradvertisement,hostprobingprovider,drivers.bmv2,pipelines.fabric,segmentrouting,t3</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRSanity/SRSanity.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRSwitchFailure/SRSwitchFailure.topo.docker b/TestON/tests/USECASE/SegmentRouting/SRSwitchFailure/SRSwitchFailure.topo.docker
new file mode 100644
index 0000000..a6f21d7
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/SRSwitchFailure/SRSwitchFailure.topo.docker
@@ -0,0 +1,39 @@
+<TOPOLOGY>
+    <COMPONENT>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <home></home>   # defines where onos home is on the build machine. Defaults to "~/onos/" if empty.
+            <COMPONENTS>
+                <useDocker>True</useDocker>  # Whether to use docker for ONOS nodes
+                <docker_prompt>~/onos#</docker_prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost>True</diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user>sdn</web_user>
+                <web_pass>rocks</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 on the target cell machine. Defaults to entry in "home" if empty.
+                <nodes>3</nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <home>~/mininet/</home>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Dockerfile b/TestON/tests/USECASE/SegmentRouting/dependencies/Dockerfile
index 4b714c5..bed2f54 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Dockerfile
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Dockerfile
@@ -5,7 +5,7 @@
 RUN ln -s /root/* .
 RUN chmod 777 $HOME
 
-RUN install_packages python-pip openvswitch-switch vim quagga isc-dhcp-server isc-dhcp-client iptables vlan
+RUN install_packages python-pip openvswitch-switch vim quagga isc-dhcp-server isc-dhcp-client iptables vlan vzctl
 RUN pip install ipaddress
 
 RUN ln -s $HOME /var/run/quagga
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
index d4a7b7e..8fa0b96 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
@@ -86,7 +86,7 @@
             main.stratumRoot = main.params[ 'DEPENDENCY'][ 'stratumRoot'] if 'stratumRoot' in main.params[ 'DEPENDENCY' ] else None
             main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
             main.maxNodes = int( main.params[ 'SCALE' ][ 'max' ] )
-            main.trellisOar = main.params[ 'DEPENDENCY' ][ 'trellisOar' ]
+            main.trellisOar = main.params[ 'DEPENDENCY' ][ 'trellisOar' ] if 'trellisOar' in main.params[ 'DEPENDENCY' ] else None
             main.t3Oar = main.params[ 'DEPENDENCY' ][ 't3Oar' ] if 't3Oar' in main.params[ 'DEPENDENCY' ] else None
 
             stepResult = main.testSetUp.envSetup( False )
@@ -141,7 +141,9 @@
             main.cleanAndExit()
 
         # Install segmentrouting and t3 app
-        appInstallResult = main.ONOSbench.onosAppInstall( main.Cluster.runningNodes[0].ipAddress, main.trellisOar)
+        appInstallResult = main.TRUE
+        if main.trellisOar:
+            appInstallResult = appInstallResult and main.ONOSbench.onosAppInstall( main.Cluster.runningNodes[0].ipAddress, main.trellisOar)
         if main.t3Oar:
             appInstallResult = appInstallResult and main.ONOSbench.onosAppInstall( main.Cluster.runningNodes[0].ipAddress, main.t3Oar)
         utilities.assert_equals( expect=main.TRUE, actual=appInstallResult,
@@ -235,18 +237,6 @@
             if 'MN_DOCKER' in main.params and main.params['MN_DOCKER']['args']:
                 destDir = "/tmp/mn_conf/"
                 # Try to ensure the destination exists
-                main.log.info( "Create folder for network config files" )
-                handle = main.Mininet1.handle
-                handle.sendline( "mkdir -p %s" % destDir )
-                handle.expect( [ main.Mininet1.prompt, main.Mininet1.dockerPrompt ] )
-                main.log.debug( handle.before + handle.after )
-                # Make sure permissions are correct
-                handle.sendline( "sudo chown %s:%s %s" % ( main.Mininet1.user_name, main.Mininet1.user_name, destDir ) )
-                handle.expect( [ main.Mininet1.prompt, main.Mininet1.dockerPrompt ] )
-                main.log.debug( handle.before + handle.after )
-                handle.sendline( "sudo chmod -R a+rwx %s" % ( destDir ) )
-                handle.expect( [ main.Mininet1.prompt, main.Mininet1.dockerPrompt ] )
-                main.log.debug( handle.before + handle.after )
             for conf in main.topologyConf.split(","):
                 # Update zebra configurations with correct ONOS instance IP
                 if conf in [ "zebradbgp1.conf", "zebradbgp2.conf" ]:
@@ -272,7 +262,7 @@
                                  onfail="Failed to copy topo files" )
         if main.stratumRoot:
             main.Mininet1.handle.sendline( "export STRATUM_ROOT=" + str( main.stratumRoot ) )
-            main.Mininet1.handle.expect( main.Mininet1.prompt )
+            main.Mininet1.handle.expect( main.Mininet1.Prompt() )
         main.step( "Starting Mininet Topology" )
         arg = "--onos-ip=%s %s" % (",".join([ctrl.ipAddress for ctrl in main.Cluster.runningNodes]), args)
         main.topology = topology
@@ -287,9 +277,11 @@
         if not topoResult:
             main.cleanAndExit()
         if main.useBmv2:
+            main.step( "Configure switches in ONOS" )
             # Upload the net-cfg file created for each switch
             filename = "onos-netcfg.json"
             switchPrefix = main.params[ 'DEPENDENCY' ].get( 'switchPrefix', "bmv2" )
+            switchNetCfg = main.TRUE
             for switch in main.Mininet1.getSwitches( switchRegex=r"(StratumBmv2Switch)|(Bmv2Switch)" ).keys():
                 path = "/tmp/mn-stratum/%s/" % switch
                 dstPath = "/tmp/"
@@ -299,6 +291,7 @@
                                      "%s%s" % ( dstPath, dstFileName ),
                                      "from" )
                 main.ONOSbench1.handle.sendline( "sudo sed -i 's/localhost/%s/g' %s%s" % ( main.Mininet1.ip_address, dstPath, dstFileName ) )
+                main.ONOSbench1.handle.expect( main.ONOSbench1.prompt )
                 # Configure managementAddress
                 main.ONOSbench1.handle.sendline( "sudo sed -i 's/localhost/%s/g' %s%s" % ( main.Mininet1.ip_address, dstPath, dstFileName ) )
                 main.ONOSbench1.handle.expect( main.ONOSbench1.prompt )
@@ -311,7 +304,19 @@
                 main.ONOSbench1.handle.sendline( "sudo sed -i '/\"basic\"/a\        \"name\": \"%s\",' %s%s" % ( switch, dstPath, dstFileName ) )
                 main.ONOSbench1.handle.expect( main.ONOSbench1.prompt )
                 main.log.debug( main.ONOSbench1.handle.before + main.ONOSbench1.handle.after )
-                main.ONOSbench1.onosNetCfg( main.ONOSserver1.ip_address, dstPath, dstFileName )
+                node = main.Cluster.active(0)
+                switchNetCfg = switchNetCfg and node.onosNetCfg( node.server.ip_address,
+                                                                 dstPath,
+                                                                 dstFileName,
+                                                                 user=node.REST.user_name,
+                                                                 password=node.REST.pwd )
+            # Stop test if we fail to push switch netcfg
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=switchNetCfg,
+                                     onpass="Successfully pushed switch netcfg",
+                                     onfail="Failed to configure switches in onos" )
+            if not switchNetCfg:
+                main.cleanAndExit()
         # Make sure hosts make some noise
         Testcaselib.discoverHosts( main )
 
@@ -1594,56 +1599,36 @@
 
             main.log.info( "Creating Mininet Docker" )
             handle = main.Mininet1.handle
-            main.Mininet1.dockerPrompt = '#'
             # build docker image
-            buildOutput = ""
-            try:
-                handle.sendline( " docker build -t trellis_mininet %s/../dependencies/" % main.testDir )
-                handle.expect( "Successfully built", timeout=600 )
-                buildOutput = handle.before + str( handle.after )
-                handle.expect( main.Mininet1.prompt )
-                buildOutput += handle.before
-                main.log.debug( buildOutput )
-            except pexpect.TIMEOUT as e:
-                main.log.error( e )
-                buildOutput += handle.before
-                main.log.debug( buildOutput )
+            dockerFilePath = "%s/../dependencies/" % main.testDir
+            dockerName = "trellis_mininet"
+            # TODO: assert on these docker calls
+            main.Mininet1.dockerBuild( dockerFilePath, dockerName )
 
             confDir = "/tmp/mn_conf/"
             # Try to ensure the destination exists
             main.log.info( "Create folder for network config files" )
             handle.sendline( "mkdir -p %s" % confDir )
-            handle.expect( main.Mininet1.prompt )
+            handle.expect( main.Mininet1.Prompt() )
             main.log.debug( handle.before + handle.after )
             # Make sure permissions are correct
             handle.sendline( "sudo chown %s:%s %s" % ( main.Mininet1.user_name, main.Mininet1.user_name, confDir ) )
-            handle.expect( main.Mininet1.prompt )
+            handle.expect( main.Mininet1.Prompt() )
             handle.sendline( "sudo chmod -R a+rwx %s" % ( confDir ) )
-            handle.expect( main.Mininet1.prompt )
+            handle.expect( main.Mininet1.Prompt() )
             main.log.debug( handle.before + handle.after )
             # Stop any leftover container
-            handle.sendline( "docker stop trellis_mininet" )
-            handle.expect( main.Mininet1.bashPrompt )
-            main.log.debug( handle.before )
+            main.Mininet1.dockerStop( dockerName )
             # Start docker container
-            handle.sendline( "docker run --name trellis_mininet %s %s" % ( main.params[ 'MN_DOCKER' ][ 'args' ], main.params[ 'MN_DOCKER' ][ 'name' ] ) )
-            i = handle.expect( [ main.Mininet1.bashPrompt, "Error response from daemon: Conflict. The container name" ] )
-            output = handle.before + handle.after
-            main.log.debug( repr(output) )
-            if i == 1:
+            runResponse = main.Mininet1.dockerRun( main.params[ 'MN_DOCKER' ][ 'name' ],
+                                                   dockerName,
+                                                   main.params[ 'MN_DOCKER' ][ 'args' ] )
+            if runResponse == main.FALSE:
                 main.log.error( "Docker container already running, aborting test" )
                 main.cleanup()
                 main.exit()
 
-            handle.sendline( "docker attach trellis_mininet" )
-            handle.expect( main.Mininet1.dockerPrompt )
-            main.log.debug( handle.before + handle.after )
-            handle.sendline( "sysctl -w net.ipv4.ip_forward=0" )
-            handle.sendline( "sysctl -w net.ipv4.conf.all.forwarding=0" )
-            handle.expect( main.Mininet1.dockerPrompt )
-            main.log.debug( handle.before + handle.after )
-            # We should be good to go
-            main.Mininet1.prompt = main.Mininet1.dockerPrompt
+            main.Mininet1.dockerAttach( dockerName, dockerPrompt='~#' )
             main.Mininet1.sudoRequired = False
 
             # Fow when we create component handles
@@ -1659,19 +1644,12 @@
 
         if hasattr( main, 'Mininet1' ):
             if 'MN_DOCKER' in main.params and main.params['MN_DOCKER']['args']:
-                main.log.info( "Deleting Mininet Docker" )
+                main.log.info( "Exiting from Mininet Docker" )
 
                 # Detach from container
                 handle = main.Mininet1.handle
                 try:
-                    handle.sendline( "exit" )  # ctrl-p ctrk-q  to detach from container
-                    main.log.debug( "sleeping %i seconds" % ( 5 ) )
-                    time.sleep(5)
-                    handle.expect( main.Mininet1.dockerPrompt )
-                    main.log.debug( handle.before + handle.after )
-                    main.Mininet1.prompt = main.Mininet1.bashPrompt
-                    handle.expect( main.Mininet1.prompt )
-                    main.log.debug( handle.before + handle.after )
+                    main.Mininet1.dockerDisconnect()
                     main.Mininet1.sudoRequired = True
                 except Exception as e:
                     main.log.error( e )
diff --git a/TestON/tests/dependencies/Cluster.py b/TestON/tests/dependencies/Cluster.py
index 9ed9256..ae08de9 100644
--- a/TestON/tests/dependencies/Cluster.py
+++ b/TestON/tests/dependencies/Cluster.py
@@ -37,7 +37,7 @@
             atomixNodes.append( "{%s:%s}" % ( node.name, node.ipAddress ) )
         return "%s[%s; Atomix Nodes:%s]" % ( self.name, ", ".join( controllers ), ", ".join( atomixNodes ) )
 
-    def __init__( self, ctrlList=[], name="Cluster" ):
+    def __init__( self, ctrlList=[], name="Cluster", useDocker=False ):
         """
             controllers : All the nodes
             runningNodes : Node that are specifically running from the test.
@@ -53,6 +53,16 @@
         self.name = str( name )
         self.atomixNodes = ctrlList
         self.iterator = iter( self.active() )
+        self.useDocker = useDocker
+        clusterParams = main.params.get( "CLUSTER", {} )
+        self.dockerSkipBuild = clusterParams.get( "dockerSkipBuild", False )
+        self.dockerBuildCmd = clusterParams.get( "dockerBuildCmd", None )
+        self.dockerBuildTimeout = int( clusterParams.get( "dockerBuildTimeout", 600 ) )
+        self.dockerFilePath = clusterParams.get( "dockerFilePath", None )
+        self.dockerImageTag = clusterParams.get( "dockerImageTag", None )
+        self.dockerOptions = clusterParams.get( "dockerOptions", "" )
+        self.atomixImageTag = clusterParams.get( "atomixImageTag", None )
+        self.atomixOptions = clusterParams.get( "atomixOptions", "" )
 
     def fromNode( self, ctrlList ):
         """
@@ -389,6 +399,88 @@
             ctrlList[ i ].active = False
         return result
 
+    def dockerStop( self, killMax, atomix=True ):
+        """
+        Description:
+            killing the onos docker containers. It will either kill the
+            current runningnodes or max number of the nodes.
+        Required:
+            * killRemoveMax - The boolean that will decide either to kill
+            only running nodes ( False ) or max number of nodes ( True ).
+        Returns:
+            Returns main.TRUE if successfully killing it.
+        """
+        getFrom = "all" if killMax else "running"
+        result = main.TRUE
+        stopResult = self.command( "dockerStop",
+                                   args=[ "name" ],
+                                   specificDriver=4,
+                                   getFrom=getFrom,
+                                   funcFromCtrl=True )
+        ctrlList = self.fromNode( getFrom )
+        for i in range( len( stopResult ) ):
+            result = result and stopResult[ i ]
+            ctrlList[ i ].active = False
+        atomixResult = main.TRUE
+        if atomix:
+            atomixResult = self.stopAtomixDocker( killMax )
+        return result and atomixResult
+
+    def dockerBuild( self, pull=True ):
+        """
+        Description:
+        Build ONOS docker image
+        Optional:
+            * pull - Try to pull latest image before building
+        Returns:
+            Returns main.TRUE if successfully killing it.
+        """
+        getFrom = "all"
+        result = main.TRUE
+        atomixResult = []
+        buildResult = []
+        if self.atomixImageTag:
+            atomixResult = self.command( "dockerPull",
+                                         args=[ self.atomixImageTag ],
+                                         specificDriver=4,
+                                         getFrom=getFrom,
+                                         funcFromCtrl=False )
+        if not self.dockerImageTag:
+            main.log.error( "No image given, exiting test" )
+            return main.FALSE
+        if pull and self.dockerImageTag:
+            buildResult = self.command( "dockerPull",
+                                        args=[ self.dockerImageTag ],
+                                        specificDriver=4,
+                                        getFrom=getFrom,
+                                        funcFromCtrl=False )
+            for i in range( len( buildResult ) ):
+                result = result and buildResult[ i ]
+        if self.dockerSkipBuild:
+            return main.TRUE
+        if not result and self.dockerBuildCmd:
+            buildResult = self.command( "makeDocker",
+                                        args=[ self.dockerFilePath, self.dockerBuildCmd ],
+                                        kwargs={ "timeout": self.dockerBuildTimeout,
+                                                 "prompt": "Successfully tagged %s" % self.dockerImageTag },
+                                        specificDriver=4,
+                                        getFrom=getFrom,
+                                        funcFromCtrl=False )
+
+        elif not result:
+            buildResult = self.command( "dockerBuild",
+                                        args=[ self.dockerFilePath, self.dockerImageTag ],
+                                        kwargs={ "timeout": self.dockerBuildTimeout,
+                                                 "pull": pull },
+                                        specificDriver=4,
+                                        getFrom=getFrom,
+                                        funcFromCtrl=False )
+        for i in range( len( atomixResult ) ):
+            result = result and atomixResult[ i ]
+        for i in range( len( buildResult ) ):
+            result = result and buildResult[ i ]
+        return result
+
     def ssh( self ):
         """
         Description:
@@ -399,9 +491,16 @@
             the onos.
         """
         result = main.TRUE
+        if self.useDocker:
+            driver = 2
+            kwargs = { "userName": "karafUser",
+                       "userPWD": "karafPass" }
+        else:
+            driver = 1
+            kwargs = { "node": "ipAddress" }
         sshResult = self.command( "onosSecureSSH",
-                                   kwargs={ "node": "ipAddress" },
-                                   specificDriver=1,
+                                   kwargs=kwargs,
+                                   specificDriver=driver,
                                    getFrom="running",
                                    funcFromCtrl=True )
         for sshR in sshResult:
@@ -417,6 +516,9 @@
             Returns main.TRUE if it successfully installed
         """
         result = main.TRUE
+        if self.useDocker:
+            # We will do this as part of startDocker
+            return result
         threads = []
         i = 0
         for ctrl in self.atomixNodes:
@@ -472,6 +574,124 @@
                 result = result and t.result
         return result
 
+    def startONOSDocker( self, installMax=True, installParallel=True ):
+        """
+        Description:
+            Installing onos via docker containers.
+        Required:
+            * installMax - True for installing max number of nodes
+            False for installing current running nodes only.
+        Returns:
+            Returns main.TRUE if it successfully installed
+        """
+        result = main.TRUE
+        threads = []
+        for ctrl in self.controllers if installMax else self.runningNodes:
+            if installParallel:
+                t = main.Thread( target=ctrl.server.dockerRun,
+                                 name="onos-run-docker-" + ctrl.name,
+                                 args=[ self.dockerImageTag, ctrl.name ],
+                                 kwargs={ "options" : self.dockerOptions } )
+                threads.append( t )
+                t.start()
+            else:
+                result = result and \
+                            ctrl.server.dockerRun( self.dockerImageTag,
+                                                   ctrl.name,
+                                                   options=self.dockerOptions )
+        if installParallel:
+            for t in threads:
+                t.join()
+                result = result and t.result
+        return result
+
+    def startAtomixDocker( self, installParallel=True ):
+        """
+        Description:
+            Installing atomix via docker containers.
+        Required:
+            * installParallel - True for installing atomix in parallel.
+        Returns:
+            Returns main.TRUE if it successfully installed
+        """
+        result = main.TRUE
+        threads = []
+        for ctrl in self.atomixNodes:
+            if installParallel:
+                t = main.Thread( target=ctrl.server.dockerRun,
+                                 name="atomix-run-docker-" + ctrl.name,
+                                 args=[ self.atomixImageTag, "atomix-" + ctrl.name ],
+                                 kwargs={ "options" : main.params['CLUSTER']['atomixOptions'],
+                                          "imageArgs": " --config /opt/atomix/conf/atomix.json --ignore-resources"} )
+                threads.append( t )
+                t.start()
+            else:
+                result = result and \
+                            ctrl.server.dockerRun( self.atomixImageTag,
+                                                   "atomix-" + ctrl.name,
+                                                   options=main.params['CLUSTER']['atomixOptions'] )
+        if installParallel:
+            for t in threads:
+                t.join()
+                result = result and t.result
+        return result
+
+    def stopAtomixDocker( self, killMax=True, installParallel=True ):
+        """
+        Description:
+            Stoping all atomix containers
+        Required:
+            * killMax - True for stoping max number of nodes
+            False for stoping current running nodes only.
+        Returns:
+            Returns main.TRUE if it successfully stoped
+        """
+        result = main.TRUE
+        threads = []
+        for ctrl in self.controllers if killMax else self.atomixNodes:
+            if installParallel:
+                t = main.Thread( target=ctrl.server.dockerStop,
+                                 name="atomix-stop-docker-" + ctrl.name,
+                                 args=[ "atomix-" + ctrl.name ] )
+                threads.append( t )
+                t.start()
+            else:
+                result = result and \
+                            ctrl.server.dockerStop( "atomix-" + ctrl.name )
+        if installParallel:
+            for t in threads:
+                t.join()
+                result = result and t.result
+        return result
+
+    def genPartitions( self, path="/tmp/cluster.json" ):
+        """
+        Description:
+           Create cluster config and move to each onos server
+        Required:
+            * installMax - True for installing max number of nodes
+            False for installing current running nodes only.
+        Returns:
+            Returns main.TRUE if it successfully installed
+        """
+        result = main.TRUE
+        # move files to onos servers
+        for ctrl in self.atomixNodes:
+            localAtomixFile = ctrl.ip_address + "-atomix.json"
+            result = result and main.ONOSbench.generateAtomixConfig( ctrl.server.ip_address, path=localAtomixFile )
+            result = result and main.ONOSbench.scp( ctrl.server,
+                                                    localAtomixFile,
+                                                    "/tmp/atomix.json",
+                                                    direction="to" )
+        for ctrl in self.controllers:
+            localOnosFile = ctrl.ip_address + "-cluster.json"
+            result = result and main.ONOSbench.generateOnosConfig( ctrl.server.ip_address, path=localOnosFile )
+            result = result and main.ONOSbench.scp( ctrl.server,
+                                                    localOnosFile,
+                                                    path,
+                                                    direction="to" )
+        return result
+
     def startCLIs( self ):
         """
         Description:
@@ -522,6 +742,16 @@
                 main.log.warn( repr( i ) )
                 currentResult = False
             results = results and currentResult
+        # Check to make sure all bundles are started
+        bundleOutput = self.command( "sendline", args=[ "bundle:list" ] )
+        for i in bundleOutput:
+            if "START LEVEL 100" in i:
+                currentResult = True
+            else:
+                currentResult = False
+                main.log.warn( "Node's bundles not fully started" )
+                main.log.debug( i )
+            results = results and currentResult
         return results
 
     def appsCheck( self, apps ):
@@ -548,6 +778,50 @@
                     main.log.warn( "{}: {} is in {} state".format( ctrl.name, app, states[ i ] ) )
         return results
 
+    def attachToONOSDocker( self ):
+        """
+        Description:
+            connect to onos docker using onosCli driver
+        Required:
+        Returns:
+            Returns main.TRUE if it successfully started.
+        """
+        getFrom = "running"
+        result = main.TRUE
+        execResults = self.command( "dockerExec",
+                                    args=[ "name" ],
+                                    kwargs={ "dockerPrompt": "dockerPrompt" },
+                                    specificDriver=2,
+                                    getFrom=getFrom,
+                                    funcFromCtrl=True )
+        ctrlList = self.fromNode( getFrom )
+        for i in range( len( execResults ) ):
+            result = result and execResults[ i ]
+            ctrlList[ i ].active = True
+        return result
+
+    def prepareForCLI( self ):
+        """
+        Description:
+            prepare docker to connect to the onos cli
+        Required:
+        Returns:
+            Returns main.TRUE if it successfully started.
+        """
+        getFrom = "running"
+        for ctrl in self.getRunningNodes():
+            ctrl.CLI.inDocker = True
+        result = main.TRUE
+        execResults = self.command( "prepareForCLI",
+                                    specificDriver=2,
+                                    getFrom=getFrom,
+                                    funcFromCtrl=True )
+        ctrlList = self.fromNode( getFrom )
+        for i in range( len( execResults ) ):
+            result = result and execResults[ i ]
+            ctrlList[ i ].active = True
+        return result
+
     def printResult( self, results, activeList, logLevel="debug" ):
         """
         Description:
@@ -623,6 +897,7 @@
                 1 - from bench
                 2 - from cli
                 3 - from rest
+                4 - from server
             * contentCheck - If this is True, it will check if the result has some
             contents.
             * getFrom - from which nodes
@@ -637,24 +912,34 @@
             Returns resultContent of the result if contentCheck
         """
         threads = []
-        drivers = [ None, "Bench", "CLI", "REST" ]
+        drivers = [ None, "Bench", "CLI", "REST", "server" ]
         results = []
         for ctrl in self.fromNode( getFrom ):
+            funcArgs = []
+            funcKwargs = {}
             try:
-                funcArgs = []
-                funcKwargs = {}
                 f = getattr( ( ctrl if not specificDriver else
                                getattr( ctrl, drivers[ specificDriver ] ) ), function )
-                if funcFromCtrl:
-                    if args:
-                        for i in range( len( args ) ):
-                            funcArgs.append( getattr( ctrl, args[ i ] ) )
-                    if kwargs:
-                        for k in kwargs:
-                            funcKwargs.update( { k: getattr( ctrl, kwargs[ k ] ) } )
             except AttributeError:
                 main.log.error( "Function " + function + " not found. Exiting the Test." )
                 main.cleanAndExit()
+            if funcFromCtrl:
+                if args:
+                    try:
+                        for i in range( len( args ) ):
+                            funcArgs.append( getattr( ctrl, args[ i ] ) )
+                    except AttributeError:
+                        main.log.error( "Argument " + str( args[ i ] ) + " for " + str( f ) + " not found. Exiting the Test." )
+                        main.cleanAndExit()
+                if kwargs:
+                    try:
+                        for k in kwargs:
+                            funcKwargs.update( { k: getattr( ctrl, kwargs[ k ] ) } )
+                    except AttributeError as e:
+                        main.log.exception("")
+                        main.log.error( "Keyword Argument " + str( k ) + " for " + str( f ) + " not found. Exiting the Test." )
+                        main.log.debug( "Passed kwargs: %s; dir(ctrl): %s" % ( repr( kwargs ), dir( ctrl ) ) )
+                        main.cleanAndExit()
             t = main.Thread( target=f,
                              name=function + "-" + ctrl.name,
                              args=funcArgs if funcFromCtrl else args,
diff --git a/TestON/tests/dependencies/ONOSSetup.py b/TestON/tests/dependencies/ONOSSetup.py
index fabfe9a..020af9d 100644
--- a/TestON/tests/dependencies/ONOSSetup.py
+++ b/TestON/tests/dependencies/ONOSSetup.py
@@ -44,7 +44,7 @@
         try:
             main.Cluster
         except ( NameError, AttributeError ):
-            main.Cluster = Cluster( main.ONOScell.nodes )
+            main.Cluster = Cluster( main.ONOScell.nodes, useDocker=main.ONOScell.useDocker )
         main.ONOSbench = main.Cluster.controllers[ 0 ].Bench
         main.testOnDirectory = re.sub( "(/tests)$", "", main.testsRoot )
 
@@ -100,20 +100,20 @@
         try:
             main.Cluster
         except ( NameError, AttributeError ):
-            main.Cluster = Cluster( main.ONOScell.nodes )
+            main.Cluster = Cluster( main.ONOScell.nodes, useDocker=main.ONOScell.useDocker )
 
         main.cellData = {}  # For creating cell file
 
         return main.TRUE
 
-    def envSetupException( self, e ):
+    def envSetupException( self, error ):
         """
         Description:
             handles the exception that might occur from the environment setup.
         Required:
-            * includeGitPull - exceeption code e.
+            * error - exception returned from except.
         """
-        main.log.exception( e )
+        main.log.exception( error )
         main.cleanAndExit()
 
     def envSetupConclusion( self, stepResult ):
@@ -219,6 +219,21 @@
         main.log.info( "Safety check, killing all ONOS processes" )
         return cluster.killOnos( killRemoveMax, stopOnos )
 
+    def killingAllOnosDocker( self, cluster, killRemoveMax ):
+        """
+        Description:
+            killing the onos docker images . It will either kill the
+            current runningnodes or max number of the nodes.
+        Required:
+            * cluster - the cluster driver that will be used.
+            * killRemoveMax - The boolean that will decide either to kill
+            only running nodes ( False ) or max number of nodes ( True ).
+        Returns:
+            Returns main.TRUE if successfully killing it.
+        """
+        main.log.info( "Safety check, stopping all ONOS docker containers" )
+        return cluster.dockerStop( killRemoveMax )
+
     def createApplyCell( self, cluster, newCell, cellName, cellApps,
                          mininetIp, useSSH, onosIps, installMax=False,
                          atomixClusterSize=None ):
@@ -243,6 +258,8 @@
         """
         if atomixClusterSize is None:
             atomixClusterSize = len( cluster.runningNodes )
+        if atomixClusterSize is 1:
+            atomixClusterSize = len( cluster.controllers )
         atomixClusterSize = int( atomixClusterSize )
         cluster.setAtomixNodes( atomixClusterSize )
         atomixIps = [ node.ipAddress for node in cluster.atomixNodes ]
@@ -316,6 +333,25 @@
             main.cleanAndExit()
         return packageResult
 
+    def buildDocker( self, cluster ):
+        """
+        Description:
+            Build the latest docker
+        Required:
+            * cluster - the cluster driver that will be used.
+        Returns:
+            Returns main.TRUE if it successfully built.
+        """
+        main.step( "Building ONOS Docker image" )
+        buildResult = cluster.dockerBuild()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=buildResult,
+                                 onpass="Successfully created ONOS docker",
+                                 onfail="Failed to create ONOS docker" )
+        if not buildResult:
+            main.cleanAndExit()
+        return buildResult
+
     def installAtomix( self, cluster, parallel=True ):
         """
         Description:
@@ -361,6 +397,42 @@
             main.cleanAndExit()
         return onosInstallResult
 
+    def startDocker( self, cluster, installMax, parallel=True ):
+        """
+        Description:
+            Start onos docker containers and verify the result
+        Required:
+            * cluster - the cluster driver that will be used.
+            * installMax - True for installing max number of nodes
+            False for installing current running nodes only.
+        Returns:
+            Returns main.TRUE if it successfully installed
+        """
+        main.step( "Create Cluster Config" )
+        configResult = cluster.genPartitions()
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=configResult,
+                                 onpass="Successfully create cluster config",
+                                 onfail="Failed to create cluster config" )
+
+        # install atomix docker containers
+        main.step( "Installing Atomix via docker containers" )
+        atomixInstallResult = cluster.startAtomixDocker( parallel )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=atomixInstallResult,
+                                 onpass="Successfully start atomix containers",
+                                 onfail="Failed to start atomix containers" )
+
+        main.step( "Installing ONOS via docker containers" )
+        onosInstallResult = cluster.startONOSDocker( installMax, parallel )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=onosInstallResult,
+                                 onpass="Successfully start ONOS containers",
+                                 onfail="Failed to start ONOS containers" )
+        if not onosInstallResult and atomixInstallResult:
+            main.cleanAndExit()
+        return onosInstallResult and atomixInstallResult
+
     def setupSsh( self, cluster ):
         """
         Description:
@@ -565,6 +637,7 @@
         if restartCluster:
             atomixKillResult = self.killingAllAtomix( cluster, killRemoveMax, stopAtomix )
             onosKillResult = self.killingAllOnos( cluster, killRemoveMax, stopOnos )
+            dockerKillResult = self.killingAllOnosDocker( cluster, killRemoveMax )
             killResult = atomixKillResult and onosKillResult
         else:
             killResult = main.TRUE
@@ -577,11 +650,24 @@
 
             packageResult = main.TRUE
             if not skipPack:
-                packageResult = self.buildOnos(cluster)
+                if cluster.useDocker:
+                    packageResult = self.buildDocker( cluster )
+                else:
+                    packageResult = self.buildOnos( cluster )
 
-            atomixInstallResult = self.installAtomix( cluster, installParallel )
-            onosInstallResult = self.installOnos( cluster, installMax, installParallel )
-            installResult = atomixInstallResult and onosInstallResult
+            if cluster.useDocker:
+                installResult = self.startDocker( cluster, installMax, installParallel )
+            else:
+                atomixInstallResult = self.installAtomix( cluster, installParallel )
+                onosInstallResult = self.installOnos( cluster, installMax, installParallel )
+                installResult = atomixInstallResult and onosInstallResult
+
+            preCLIResult = main.TRUE
+            if cluster.useDocker:
+                attachResult = cluster.attachToONOSDocker()
+                prepareResult = cluster.prepareForCLI()
+
+                preCLIResult = preCLIResult and attachResult and prepareResult
 
             self.processList( extraClean, cleanArgs )
             secureSshResult = self.setupSsh( cluster )
@@ -590,8 +676,11 @@
             uninstallResult = main.TRUE
             installResult = main.TRUE
             secureSshResult = main.TRUE
+            preCLIResult = main.TRUE
 
-        onosServiceResult = self.checkOnosService( cluster )
+        onosServiceResult = main.TRUE
+        if not cluster.useDocker:
+            onosServiceResult = self.checkOnosService( cluster )
 
         onosCliResult = main.TRUE
         if startOnosCli:
@@ -604,6 +693,11 @@
             if apps:
                 apps = apps.split( ',' )
                 apps = [ appPrefix + app for app in apps ]
+                if cluster.useDocker:
+                    node = main.Cluster.active( 0 )
+                    for app in apps:
+                        node.activateApp( app )
+
                 onosAppsResult = self.checkOnosApps( cluster, apps )
             else:
                 main.log.warn( "No apps were specified to be checked after startup" )
@@ -616,4 +710,4 @@
 
         return killResult and cellResult and packageResult and uninstallResult and \
                installResult and secureSshResult and onosServiceResult and onosCliResult and \
-               onosNodesResult and onosAppsResult
+               onosNodesResult and onosAppsResult and preCLIResult
diff --git a/TestON/tests/dependencies/utils.py b/TestON/tests/dependencies/utils.py
index 6537afc..3cf849a 100644
--- a/TestON/tests/dependencies/utils.py
+++ b/TestON/tests/dependencies/utils.py
@@ -74,10 +74,20 @@
         scpResult = main.TRUE
         copyResult = main.TRUE
         for ctrl in main.Cluster.runningNodes:
-            scpResult = scpResult and main.ONOSbench.scp( ctrl,
-                                                          "/opt/onos/log/karaf.log",
-                                                          "/tmp/karaf.log",
-                                                          direction="from" )
+            if ctrl.inDocker:
+                scpResult = scpResult and ctrl.server.dockerCp( ctrl.name,
+                                                                "/opt/onos/log/karaf.log",
+                                                                "/tmp/karaf.log",
+                                                                direction="from" )
+                scpResult = scpResult and main.ONOSbench.scp( ctrl.server,
+                                                              "/tmp/karaf.log",
+                                                              "/tmp/karaf.log",
+                                                              direction="from" )
+            else:
+                scpResult = scpResult and main.ONOSbench.scp( ctrl,
+                                                              "/opt/onos/log/karaf.log",
+                                                              "/tmp/karaf.log",
+                                                              direction="from" )
             copyResult = copyResult and main.ONOSbench.cpLogsToDir( "/tmp/karaf.log", main.logdir,
                                                                     copyFileName=( copyFileName + "_karaf.log." +
                                                                                    ctrl.name + "_" ) if before else