Updating drivers

Changes Include:
    * Modifying some log levels
    * Remove some IDE injected code
    * Fix for some disconnect bug which resulted in a loop of disconnect calls
    * Add json support for Leaders in onosclidriver
    * Add json support for pendingMap in onosclidriver
    * Add json support for partitions in onosclidriver
    * Add json support for nodes in onosclidriver
    * In git pull function, add option to fast forward only and set as default
    * Add more expects in git pull, mostly for when pulling results in a merge
    * Add another expect for onosStop function
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index dc9de05..a6436ca 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -326,7 +326,7 @@
         command = args[ "SRC" ] + " ping " + \
             args[ "TARGET" ] + " -c 1 -i 1 -W 8"
         try:
-            main.log.warn( "Sending: " + command )
+            main.log.info( "Sending: " + command )
             self.handle.sendline( command )
             i = self.handle.expect( [ command, pexpect.TIMEOUT ] )
             if i == 1:
diff --git a/TestON/drivers/common/cli/emulator/remotemininetdriver.py b/TestON/drivers/common/cli/emulator/remotemininetdriver.py
index 8e445a9..e451335 100644
--- a/TestON/drivers/common/cli/emulator/remotemininetdriver.py
+++ b/TestON/drivers/common/cli/emulator/remotemininetdriver.py
@@ -299,7 +299,7 @@
             self.handle.sendline( "" )
             i = self.handle.expect( [ 'No\ssuch\device', 'listening\son',
                                     pexpect.TIMEOUT, "\$" ], timeout=10 )
-            main.log.warn( self.handle.before + self.handle.after )
+            main.log.info( self.handle.before + self.handle.after )
             if i == 0:
                 main.log.error( self.name + ": tcpdump - No such device exists.\
                         tcpdump attempted on: " + intf )
@@ -591,7 +591,6 @@
                 if actionType == 'remove':
                     # -D is the 'delete' rule of iptables
                     actionRemove = '-D'
-                    # noinspection PyBroadException
                     try:
                         self.handle.sendline( "" )
                         # Delete a specific rule specified into the function
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index 7146211..be5e425 100644
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -84,19 +84,23 @@
         Called when Test is complete to disconnect the ONOS handle.
         """
         response = main.TRUE
-        # noinspection PyBroadException
         try:
-            self.logout()
-            self.handle.sendline( "" )
-            self.handle.expect( "\$" )
-            self.handle.sendline( "exit" )
-            self.handle.expect( "closed" )
+            if self.handle:
+                i = self.logout()
+                if i == main.TRUE:
+                    self.handle.sendline( "" )
+                    self.handle.expect( "\$" )
+                    self.handle.sendline( "exit" )
+                    self.handle.expect( "closed" )
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             response = main.FALSE
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":     " + self.handle.before )
+        except ValueError:
+            main.log.exception( "Exception in discconect of " + self.name )
+            response = main.TRUE
         except Exception:
             main.log.exception( self.name + ": Connection failed to the host" )
             response = main.FALSE
@@ -105,27 +109,36 @@
     def logout( self ):
         """
         Sends 'logout' command to ONOS cli
+        Returns main.TRUE if exited CLI and
+                main.FALSE on timeout (not guranteed you are disconnected)
+                None on TypeError
+                Exits test on unknown error or pexpect exits unexpectedly
         """
         try:
-            self.handle.sendline( "" )
-            i = self.handle.expect( [ "onos>", "\$", pexpect.TIMEOUT ],
-                                    timeout=10 )
-            if i == 0:  # In ONOS CLI
-                self.handle.sendline( "logout" )
-                self.handle.expect( "\$" )
-            elif i == 1:  # not in CLI
+            if self.handle:
+                self.handle.sendline( "" )
+                i = self.handle.expect( [ "onos>", "\$", pexpect.TIMEOUT ],
+                                        timeout=10 )
+                if i == 0:  # In ONOS CLI
+                    self.handle.sendline( "logout" )
+                    self.handle.expect( "\$" )
+                    return main.TRUE
+                elif i == 1:  # not in CLI
+                    return main.TRUE
+                elif i == 3:  # Timeout
+                    return main.FALSE
+            else:
                 return main.TRUE
-            elif i == 3:  # Timeout
-                return main.FALSE
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": eof exception found" )
-            main.log.error( self.name + ":    " +
-                            self.handle.before )
+            main.log.error( self.name + ":    " + self.handle.before )
             main.cleanup()
             main.exit()
+        except ValueError:
+            main.log.error( self.name + "ValueError exception in logout method" )
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
@@ -349,7 +362,6 @@
         Optional:
             * tcpPort
         """
-        # noinspection PyBroadException
         try:
             cmdStr = "add-node " + str( nodeId ) + " " +\
                 str( ONOSIp ) + " " + str( tcpPort )
@@ -402,16 +414,24 @@
             main.cleanup()
             main.exit()
 
-    def nodes( self ):
+    def nodes( self, jsonFormat=True):
         """
         List the nodes currently visible
         Issues command: 'nodes'
-        Returns: entire handle of list of nodes
+        Optional argument:
+            * jsonFormat - boolean indicating if you want output in json
         """
         try:
-            cmdStr = "nodes"
-            handle = self.sendline( cmdStr )
-            return handle
+            if jsonFormat:
+                cmdStr = "nodes -j"
+                output = self.sendline( cmdStr )
+                ansiEscape = re.compile( r'\r\r\n\x1b[^m]*m' )
+                parsedOutput = ansiEscape.sub( '', output )
+                return parsedOutput
+            else:
+                cmdStr = "nodes"
+                output = self.sendline( cmdStr )
+                return output
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
@@ -938,6 +958,7 @@
             handle = self.sendline( cmdStr )
             if re.search( "Error", handle ):
                 main.log.error( "Error in adding Host intent" )
+                main.log.debug( "Response from ONOS was: " + repr( handle ) )
                 return None
             else:
                 main.log.info( "Host intent installed between " +
@@ -947,6 +968,8 @@
                     return match.group()[3:-1]
                 else:
                     main.log.error( "Error, intent ID not found" )
+                    main.log.debug( "Response from ONOS was: " +
+                                    repr( handle ) )
                     return None
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
@@ -1299,10 +1322,10 @@
                       purge=False, sync=False ):
         """
         Remove intent for specified application id and intent id
-        Optional args:- 
+        Optional args:-
         -s or --sync: Waits for the removal before returning
-        -p or --purge: Purge the intent from the store after removal  
-        
+        -p or --purge: Purge the intent from the store after removal
+
         Returns:
             main.False on error and
             cli output otherwise
@@ -1472,7 +1495,7 @@
             else:
                 cmdStr = "flows"
                 handle = self.sendline( cmdStr )
-            if re.search( "Error\sexecuting\scommand:", handle ):
+            if re.search( "Error:", handle ):
                 main.log.error( self.name + ".flows() response: " +
                                 str( handle ) )
             return handle
@@ -2171,15 +2194,31 @@
             main.cleanup()
             main.exit()
 
-    def leaders( self ):
+    def leaders( self, jsonFormat=True ):
         """
         Returns the output of the leaders command.
+        Optional argument:
+            * jsonFormat - boolean indicating if you want output in json
         """
         # FIXME: add json output
+        # Sample JSON
+        # {
+        #     "electedTime": "13m ago",
+        #     "epoch": 4,
+        #     "leader": "10.128.30.17",
+        #     "topic": "intent-partition-3"
+        #  },
         try:
-            output = self.sendline( "onos:leaders" )
-            main.log.warn( output )
-            return output
+            if jsonFormat:
+                cmdStr = "onos:leaders -j"
+                output = self.sendline( cmdStr )
+                ansiEscape = re.compile( r'\r\r\n\x1b[^m]*m' )
+                cleanedOutput = ansiEscape.sub( '', output )
+                return cleanedOutput
+            else:
+                cmdStr = "onos:leaders"
+                output = self.sendline( cmdStr )
+                return output
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
@@ -2193,15 +2232,21 @@
             main.cleanup()
             main.exit()
 
-    def pendingMap( self ):
+    def pendingMap( self, jsonFormat=True ):
         """
         Returns the output of the intent Pending map.
         """
-        # FIXME: add json output
         try:
-            output = self.sendline( "onos:intents -p" )
-            main.log.warn( output )
-            return output
+            if jsonFormat:
+                cmdStr = "onos:intents -p -j"
+                output = self.sendline( cmdStr )
+                ansiEscape = re.compile( r'\r\r\n\x1b[^m]*m' )
+                cleanedOutput = ansiEscape.sub( '', output )
+                return cleanedOutput
+            else:
+                cmdStr = "onos:intents -p"
+                output = self.sendline( cmdStr )
+                return output
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
@@ -2215,15 +2260,32 @@
             main.cleanup()
             main.exit()
 
-    def partitions( self ):
+    def partitions( self, jsonFormat=True ):
         """
         Returns the output of the raft partitions command for ONOS.
         """
-        # FIXME: add json output
+        # Sample JSON
+        # {
+        #     "leader": "tcp://10.128.30.11:7238",
+        #     "members": [
+        #         "tcp://10.128.30.11:7238",
+        #         "tcp://10.128.30.17:7238",
+        #         "tcp://10.128.30.13:7238",
+        #     ],
+        #     "name": "p1",
+        #     "term": 3
+        # },
         try:
-            output = self.sendline( "partitions" )
-            main.log.warn( output )
-            return output
+            if jsonFormat:
+                cmdStr = "onos:partitions -j"
+                output = self.sendline( cmdStr )
+                ansiEscape = re.compile( r'\r\r\n\x1b[^m]*m' )
+                cleanedOutput = ansiEscape.sub( '', output )
+                return cleanedOutput
+            else:
+                cmdStr = "onos:partitions"
+                output = self.sendline( cmdStr )
+                return output
         except TypeError:
             main.log.exception( self.name + ": Object not as expected" )
             return None
diff --git a/TestON/drivers/common/cli/onosdriver.py b/TestON/drivers/common/cli/onosdriver.py
index 05044c6..1c7d329 100644
--- a/TestON/drivers/common/cli/onosdriver.py
+++ b/TestON/drivers/common/cli/onosdriver.py
@@ -81,13 +81,17 @@
         """
         response = main.TRUE
         try:
-            self.handle.sendline( "" )
-            self.handle.expect( "\$" )
-            self.handle.sendline( "exit" )
-            self.handle.expect( "closed" )
+            if self.handle:
+                self.handle.sendline( "" )
+                self.handle.expect( "\$" )
+                self.handle.sendline( "exit" )
+                self.handle.expect( "closed" )
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":     " + self.handle.before )
+        except ValueError:
+            main.log.exception( "Exception in discconect of " + self.name )
+            response = main.TRUE
         except Exception:
             main.log.exception( self.name + ": Connection failed to the host" )
             response = main.FALSE
@@ -220,10 +224,14 @@
             main.cleanup()
             main.exit()
 
-    def gitPull( self, comp1="" ):
+    def gitPull( self, comp1="", fastForward=True ):
         """
         Assumes that "git pull" works without login
 
+        If the fastForward boolean is set to true, only git pulls that can
+        be fast forwarded will be performed. IE if you have not local commits
+        in your branch.
+
         This function will perform a git pull on the ONOS instance.
         If used as gitPull( "NODE" ) it will do git pull + NODE. This is
         for the purpose of pulling from other nodes if necessary.
@@ -239,11 +247,12 @@
             # self.stop()
             self.handle.sendline( "cd " + self.home )
             self.handle.expect( self.home + "\$" )
-            if comp1 == "":
-                self.handle.sendline( "git pull" )
-            else:
-                self.handle.sendline( "git pull " + comp1 )
-
+            cmd = "git pull"
+            if comp1 != "":
+                cmd += ' ' +  comp1
+            if fastForward:
+                cmd += ' ' + " --ff-only"
+            self.handle.sendline( cmd )
             i = self.handle.expect(
                 [
                     'fatal',
@@ -254,6 +263,9 @@
                     'You\sare\snot\scurrently\son\sa\sbranch',
                     'You asked me to pull without telling me which branch you',
                     'Pull is not possible because you have unmerged files',
+                    'Please enter a commit message to explain why this merge',
+                    'Found a swap file by the name',
+                    'Please, commit your changes before you can merge.',
                     pexpect.TIMEOUT ],
                 timeout=300 )
             # debug
@@ -261,7 +273,11 @@
             # "git pull response: " +
             # str( self.handle.before ) + str( self.handle.after ) )
             if i == 0:
-                main.log.error( self.name + ": Git pull had some issue..." )
+                main.log.error( self.name + ": Git pull had some issue" )
+                output = self.handle.after
+                self.handle.expect( '\$' )
+                output += self.handle.before
+                main.log.warn( output )
                 return main.ERROR
             elif i == 1:
                 main.log.error(
@@ -303,6 +319,29 @@
                     "you have unmerged files." )
                 return main.ERROR
             elif i == 8:
+                # NOTE: abandoning test since we can't reliably handle this
+                #       there could be different default text editors and we
+                #       also don't know if we actually want to make the commit
+                main.log.error( "Git pull resulted in a merge commit message" +
+                                ". Exiting test!" )
+                main.cleanup()
+                main.exit()
+            elif i == 9:  # Merge commit message but swap file exists
+                main.log.error( "Git pull resulted in a merge commit message" +
+                                " but a swap file exists." )
+                try:
+                    self.handle.send( 'A' )  # Abort
+                    self.handle.expect( "\$" )
+                    return main.ERROR
+                except Exception:
+                    main.log.exception( "Couldn't exit editor prompt!")
+                    main.cleanup()
+                    main.exit()
+            elif i == 10:  # In the middle of a merge commit
+                main.log.error( "Git branch is in the middle of a merge. " )
+                main.log.warn( self.handle.before + self.handle.after )
+                return main.ERROR
+            elif i == 11:
                 main.log.error( self.name + ": Git Pull - TIMEOUT" )
                 main.log.error(
                     self.name + " Response was: " + str(
@@ -348,9 +387,9 @@
             self.handle.expect( cmd )
             i = self.handle.expect(
                 [ 'fatal',
-                  'Username\sfor\s(.*):\s',
-                  'Already\son\s\'',
-                  'Switched\sto\sbranch\s\'' + str( branch ),
+                  'Username for (.*): ',
+                  'Already on \'',
+                  'Switched to branch \'' + str( branch ),
                   pexpect.TIMEOUT,
                   'error: Your local changes to the following files' +
                   'would be overwritten by checkout:',
@@ -805,6 +844,7 @@
                                   " stop" )
             i = self.handle.expect( [
                 "stop/waiting",
+                "Could not resolve hostname",
                 "Unknown\sinstance",
                 pexpect.TIMEOUT ], timeout=60 )
 
@@ -815,6 +855,9 @@
                 main.log.info( "onosStop() Unknown ONOS instance specified: " +
                                str( nodeIp ) )
                 return main.FALSE
+            elif i == 2:
+                main.log.warn( "ONOS wasn't running" )
+                return main.TRUE
             else:
                 main.log.error( "ONOS service failed to stop" )
                 return main.FALSE