[SDFAB-1024] Modify TestON to verify persistence mastership

Additionally, improve ONOS cli driver

Change-Id: I92a6908fc5e76cdc6538dccf5845ee5a6e99662a
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index 5694fc6..f4f65db 100755
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -2676,16 +2676,25 @@
             else:
                 main.cleanAndExit()
 
-    def flows( self, state="any", jsonFormat=True, timeout=60, noExit=False, noCore=False, device=""):
+    def flows( self, state="any", jsonFormat=True, timeout=60, noExit=False, noCore=False, device="" ):
+        return self.dataPlaneEntities( entity="flows", state=state, jsonFormat=jsonFormat,
+            timeout=timeout, noExit=noExit, noCore=noCore, device=device )
+
+    def groups( self, state="any", jsonFormat=True, timeout=60, noExit=False, noCore=False, device="" ):
+        return self.dataPlaneEntities( entity="groups", state=state, jsonFormat=jsonFormat,
+            timeout=timeout, noExit=noExit, noCore=noCore, device=device )
+
+    def dataPlaneEntities( self, entity="flows", state="any", jsonFormat=True,
+        timeout=60, noExit=False, noCore=False, device="" ):
         """
         Optional:
             * jsonFormat: enable output formatting in json
             * noCore: suppress core flows
         Description:
-            Obtain flows currently installed
+            Obtain dataplaneEntities currently installed
         """
         try:
-            cmdStr = "flows"
+            cmdStr = entity
             if jsonFormat:
                 cmdStr += " -j"
             if noCore:
@@ -2696,7 +2705,7 @@
             assert handle is not None, "Error in sendline"
             assert "Command not found:" not in handle, handle
             if re.search( "Error:", handle ):
-                main.log.error( self.name + ": flows() response: " +
+                main.log.error( self.name + ": " + entity + "() response: " +
                                 str( handle ) )
             return handle
         except AssertionError:
@@ -2782,6 +2791,68 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
 
+    def checkGroupCount( self, min=0, timeout=60 ):
+        count = self.getTotalGroupsNum( timeout=timeout )
+        count = int( count ) if count else 0
+        main.log.debug( "found {} groups".format( count ) )
+        return count if ( count >= min ) else False
+
+    def checkGroupsState( self, isPENDING=True, timeout=60, noExit=False ):
+        """
+        Description:
+            Check the if all the current groups are in ADDED state
+            We check PENDING_ADD, PENDING_REMOVE, REMOVED, and FAILED groups,
+            if the count of those states is 0, which means all current groups
+            are in ADDED state, and return main.TRUE otherwise return main.FALSE
+        Optional:
+            * isPENDING:  whether the PENDING_ADD is also a correct status
+        Return:
+            returnValue - Returns main.TRUE only if all groups are in
+                          ADDED state or PENDING_ADD if the isPENDING
+                          parameter is set true, return main.FALSE otherwise.
+        """
+        try:
+            states = [ "PENDING_ADD", "PENDING_ADD_RETRY", "PENDING_DELETE", "PENDING_UPDATE", "WAITING_AUDIT_COMPLETE" ]
+            checkedStates = []
+            statesCount = [ 0, 0, 0, 0, 0 ]
+            for s in states:
+                rawGroups = self.groups( state=s, timeout = timeout )
+                if rawGroups:
+                    # if we didn't get groups or groups function return None, we should return
+                    # main.Flase
+                    checkedStates.append( json.loads( rawGroups ) )
+                else:
+                    return main.FALSE
+            for i in range( len( states ) ):
+                statesCount[ i ] += int( len(checkedStates[i]) )
+                main.log.info( states[ i ] + " groups: " + str( statesCount[ i ] ) )
+
+            # We want to count PENDING_ADD if isPENDING is true
+            if isPENDING:
+                if statesCount[ 2 ] + statesCount[ 3 ] + statesCount[ 4 ] > 0:
+                    return main.FALSE
+            else:
+                if statesCount[ 0 ] + statesCount[ 1 ] + statesCount[ 2 ] + statesCount[ 3 ] + statesCount[ 4 ] > 0:
+                    return main.FALSE
+            return main.TRUE
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawGroups ) )
+            return None
+
+        except AssertionError:
+            main.log.exception( "" )
+            return None
+        except pexpect.TIMEOUT:
+            main.log.error( self.name + ": ONOS timeout" )
+            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 pushTestIntents( self, ingress, egress, batchSize, offset="",
                          options="", timeout=10, background = False, noExit=False, getResponse=False ):
         """
@@ -2843,31 +2914,37 @@
             main.cleanAndExit()
 
     def getTotalFlowsNum( self, timeout=60, noExit=False ):
+        return self.getTotalEntitiesNum( entity="flows", timeout=60, noExit=False )
+
+    def getTotalGroupsNum( self, timeout=60, noExit=False ):
+        return self.getTotalEntitiesNum( entity="groups", timeout=60, noExit=False )
+
+    def getTotalEntitiesNum( self, entity="flows", timeout=60, noExit=False ):
         """
         Description:
-            Get the number of ADDED flows.
+            Get the number of ADDED entities.
         Return:
-            The number of ADDED flows
+            The number of ADDED entities
             Or return None if any exceptions
         """
 
         try:
-            # get total added flows number
-            cmd = "flows -c added"
-            rawFlows = self.sendline( cmd, timeout=timeout, noExit=noExit )
-            if rawFlows:
-                rawFlows = rawFlows.split( "\n" )
-                totalFlows = 0
-                for l in rawFlows:
-                    totalFlows += int( l.split( "Count=" )[ 1 ] )
+            # get total added entities number
+            cmd = entity + " -c added"
+            rawEntities = self.sendline( cmd, timeout=timeout, noExit=noExit )
+            if rawEntities:
+                rawEntities = rawEntities.split( "\n" )
+                totalEntities = 0
+                for l in rawEntities:
+                    totalEntities += int( l.split( "Count=" )[ 1 ] )
             else:
                 main.log.warn( "Response not as expected!" )
                 return None
-            return totalFlows
+            return totalEntities
 
         except IndexError:
             main.log.exception( "{}: Object not as expected!".format( self.name ) )
-            main.log.debug( "rawFlows: {}".format( rawFlows ) )
+            main.log.debug( "rawEntities: {}".format( rawEntities ) )
             return None
         except ( TypeError, ValueError ):
             main.log.exception( "{}: Object not as expected!".format( self.name ) )