Merge "Add VLAN Treatment Intent Tests to FUNCintent for Host, Point, MPSP, and SPMP intents."
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index 4d8cec7..345e0f9 100644
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -151,7 +151,10 @@
                         # ONOS didn't fully load, and logout command isn't working
                         # or the command timed out
                         self.handle.send( "\x04" )  # send ctrl-d
-                        self.handle.expect( "\$" )
+                        try:
+                            self.handle.expect( "\$" )
+                        except pexpect.TIMEOUT:
+                            main.log.error( "ONOS did not respond to 'logout' or CTRL-d" )
                         return main.TRUE
                     else: # some other output
                         main.log.warn( "Unknown repsonse to logout command: '{}'",
@@ -2039,19 +2042,18 @@
 
     def getIntentState(self, intentsId, intentsJson=None):
         """
-            Check intent state.
-            Accepts a single intent ID (string type) or a list of intent IDs.
-            Returns the state(string type) of the id if a single intent ID is
-            accepted.
-            Returns a dictionary with intent IDs as the key and its
-            corresponding states as the values
-            Parameters:
-            intentId: intent ID (string type)
+        Description:
+            Gets intent state. Accepts a single intent ID (string type) or a
+            list of intent IDs.
+        Parameters:
+            intentsId: intent ID, both string type and list type are acceptable
             intentsJson: parsed json object from the onos:intents api
-            Returns:
-            state = An intent's state- INSTALL,WITHDRAWN etc.
-            stateDict = Dictionary of intent's state. intent ID as the keys and
-            state as the values.
+        Returns:
+            Returns the state (string type) of the ID if a single intent ID is
+            accepted.
+            Returns a list of dictionaries if a list of intent IDs is accepted,
+            and each dictionary maps 'id' to the Intent ID and 'state' to
+            corresponding intent state.
         """
         try:
             state = "State is Undefined"
@@ -2163,6 +2165,52 @@
             main.cleanup()
             main.exit()
 
+    def compareIntent( self, intentDict ):
+        """
+        Description:
+            Compare the intent ids and states provided in the argument with all intents in ONOS
+        Return:
+            Returns main.TRUE if the two sets of intents match exactly, otherwise main.FALSE
+        Arguments:
+            intentDict: a dictionary which maps intent ids to intent states
+        """
+        try:
+            intentsRaw = self.intents()
+            intentsJson = json.loads( intentsRaw )
+            intentDictONOS = {}
+            for intent in intentsJson:
+                intentDictONOS[ intent[ 'id' ] ] = intent[ 'state' ]
+            if len( intentDict ) != len( intentDictONOS ):
+                main.log.info( self.name + ": expected intent count does not match that in ONOS, " +
+                               str( len( intentDict ) ) + " expected and " +
+                               str( len( intentDictONOS ) ) + " actual" )
+                return main.FALSE
+            returnValue = main.TRUE
+            for intentID in intentDict.keys():
+                if not intentID in intentDictONOS.keys():
+                    main.log.debug( self.name + ": intent ID - " + intentID + " is not in ONOS" )
+                    returnValue = main.FALSE
+                elif intentDict[ intentID ] != intentDictONOS[ intentID ]:
+                    main.log.debug( self.name + ": intent ID - " + intentID +
+                                    " expected state is " + intentDict[ intentID ] +
+                                    " but actual state is " + intentDictONOS[ intentID ] )
+                    returnValue = main.FALSE
+            if returnValue == main.TRUE:
+                main.log.info( self.name + ": all intent IDs and states match that in ONOS" )
+            return returnValue
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, intentsRaw ) )
+            return None
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
     def checkIntentSummary( self, timeout=60 ):
         """
         Description:
@@ -2405,8 +2453,8 @@
 
             return int(totalFlows)
 
-        except TypeError:
-            main.log.exception( self.name + ": Object not as expected" )
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawFlows ) )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2435,8 +2483,8 @@
                 return  -1
             response = json.loads( response )
             return int( response.get("intents") )
-        except TypeError:
-            main.log.exception( self.name + ": Object not as expected" )
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, response ) )
             return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
@@ -2697,6 +2745,9 @@
             topology = json.loads(topologyOutput)
             main.log.debug( topology )
             return topology
+        except ( TypeError, ValueError ):
+            main.log.exception( "{}: Object not as expected: {!r}".format( self.name, topologyOutput ) )
+            return None
         except pexpect.EOF:
             main.log.error( self.name + ": EOF exception found" )
             main.log.error( self.name + ":    " + self.handle.before )
diff --git a/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py b/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py
index 699a17d..6831811 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/elements/ONOSElement.py
@@ -33,6 +33,7 @@
         self.default = ''
         self.type = 'INTENT'
         self.id = id
+        self.expectedState = 'INSTALLED'
 
     def isHostIntent( self ):
         return self.type == 'INTENT_HOST'
@@ -40,12 +41,26 @@
     def isPointIntent( self ):
         return self.type == 'INTENT_POINT'
 
+    def isFailed( self ):
+        return self.expectedState == 'FAILED'
+
+    def isInstalled( self ):
+        return self.expectedState == 'INSTALLED'
+
+    def setFailed( self ):
+        self.expectedState = 'FAILED'
+
+    def setInstalled( self ):
+        self.expectedState = 'INSTALLED'
+
 class HostIntent( Intent ):
     def __init__( self, id, hostA, hostB ):
         Intent.__init__( self, id )
         self.type = 'INTENT_HOST'
         self.hostA = hostA
         self.hostB = hostB
+        self.deviceA = hostA.device
+        self.deviceB = hostB.device
 
     def __str__( self ):
         return "ID: " + self.id
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
index e48f674..01aa734 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -25,21 +25,15 @@
 
     def startCheckEvent( self, args=None ):
         checkResult = EventStates().PASS
-        intentIDs = []
+        # TODO: check intents that are expected in "FAILED" state?
+        installedIntentIDs = []
         for intent in main.intents:
-            if intent.isHostIntent():
-                deviceA = intent.hostA.device
-                deviceB = intent.hostB.device
-            elif intent.isPointIntent():
-                deviceA = intent.deviceA
-                deviceB = intent.deviceB
-            # Exclude the intents that are to or from removed devices/hosts
-            if not deviceA.isRemoved() and not deviceB.isRemoved():
-                intentIDs.append( intent.id )
+            if intent.isInstalled():
+                installedIntentIDs.append( intent.id )
         for controller in main.controllers:
             if controller.isUp():
                 with controller.CLILock:
-                    intentState = controller.CLI.checkIntentState( intentsId=intentIDs )
+                    intentState = controller.CLI.checkIntentState( intentsId=installedIntentIDs )
                 if not intentState:
                     main.log.warn( "Intent Check - Not all intents are in INSTALLED state on ONOS%s" % ( controller.index ) )
                     checkResult = EventStates().FAIL
@@ -141,10 +135,10 @@
                         dpidToMaster[ dpid ] = device[ 'master' ]
                     elif dpidToMaster[ dpid ] != device[ 'master' ]:
                         checkResult = EventStates().FAIL
-                        main.log.warn( "ONOS Check - Mastership of %s on ONOS%s is inconsistent with that on ONOS1" % ( device.name, controller.index ) )
+                        main.log.warn( "ONOS Check - Mastership of %s on ONOS%s is inconsistent with that on ONOS1" % ( dpid, controller.index ) )
                     if dpidToAvailability[ dpid ] and device[ 'master' ] == "none":
                         checkResult = EventStates().FAIL
-                        main.log.warn( "ONOS Check - Device %s has no master on ONOS%s" % ( device.name, controller.index ) )
+                        main.log.warn( "ONOS Check - Device %s has no master on ONOS%s" % ( dpid, controller.index ) )
                 # Check leaders
                 with controller.CLILock:
                     leaders = controller.CLI.leaders()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
index 46b37e7..7247ecf 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -208,6 +208,9 @@
                 link.backwardLink.setRemoved()
             for host in self.device.hosts:
                 host.setRemoved()
+            for intent in main.intents:
+                if intent.deviceA == self.device or intent.deviceB == self.device:
+                    intent.setFailed()
         return EventStates().PASS
 
 class DeviceUp( DeviceEvent ):
@@ -256,6 +259,11 @@
             with main.variableLock:
                 link.bringUp()
                 link.backwardLink.bringUp()
+                for intent in main.intents:
+                    if intent.isFailed():
+                        if intent.deviceA == self.device and intent.deviceB.isUp() or\
+                        intent.deviceB == self.device and intent.deviceA.isUp():
+                            intent.setInstalled()
         # Re-assign mastership for the device
         with main.mininetLock:
             main.Mininet1.assignSwController( sw=self.device.name, ip=main.onosIPs )
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
index 6aee29e..608f0e6 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
@@ -419,6 +419,8 @@
 
         # make sure the checkInterval divisible batchSize
         main.checkInterval = int( int( main.checkInterval / main.batchSize ) * main.batchSize )
+        flowTemp=0
+        totalFlows=0
         for i in range(limit):
 
             # Threads pool
@@ -463,22 +465,27 @@
                     temp = 0
                     intentsState = main.CLIs[0].checkIntentSummary(timeout=600)
                     if intentsState:
-                        totalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
-                        if temp < totalIntents:
-                            temp = totalIntents
+                        verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                        if temp < verifyTotalIntents:
+                            temp = verifyTotalIntents 
                         else:
-                            totalIntents = temp
+                            verifytotalIntents = temp
+                        main.log.info("Total Intents: {}".format( verifyTotalIntents ) )
                         break
-                        main.log.info("Total Intents: {}".format( totalIntents) )
                     k = k+1
-                
+
+                totalFlows = main.CLIs[0].getTotalFlowsNum( timeout=600, noExit=True )
+                if flowTemp < totalFlows:
+                    flowTemp = totalFlows
+                else:
+                    totalFlows = flowTemp 
+
                 if not intentsState:
                     # If some intents are not installed, grep the previous flows list, and finished this test case
                     main.log.warn( "Some intens did not install" )
-                    main.log.info("Total Intents: {}".format( totalIntents) )
+                    main.log.info("Total Intents: {}".format( verifyTotalIntents) )
                     break
 
-        totalFlows = main.CLIs[0].getTotalFlowsNum(timeout=600, noExit=True)
         del main.scale[0]
         utilities.assert_equals( expect = main.TRUE,
                                  actual = intentsState,
@@ -496,7 +503,7 @@
             temp = str(main.numCtrls)
             temp += ",'" + "baremetal1" + "'"
             # how many intents we installed before crash
-            temp += "," + str(totalIntents)
+            temp += "," + str(verifyTotalIntents)
             # how many flows we installed before crash
             temp += "," + str(totalFlows)
             # other columns in database, but we didn't use in this test
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
index 7e6753e..97dc6f3 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntentsWithFlowObj/SCPFscalingMaxIntentsWithFlowObj.py
@@ -421,6 +421,9 @@
 
         # make sure the checkInterval divisible batchSize
         main.checkInterval = int( int( main.checkInterval / main.batchSize ) * main.batchSize )
+        flowTemp=0
+        totalFlows=0
+        verifyTotalIntents=0
         for i in range(limit):
 
             # Threads pool
@@ -465,22 +468,27 @@
                     temp = 0
                     intentsState = main.CLIs[0].checkIntentSummary(timeout=600)
                     if intentsState:
-                        totalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
-                        if temp < totalIntents:
-                            temp = totalIntents
+                        verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600)
+                        if temp < verifyTotalIntents:
+                            temp = verifyTotalIntents
                         else:
-                            totalIntents = temp
+                            verifyTotalIntents = temp
                         break
                         main.log.info("Total Intents: {}".format( totalIntents) )
                     k = k+1
-                
+
+                totalFlows = main.CLIs[0].getTotalFlowsNum( timeout=600, noExit=True )
+                if flowTemp<totalFlows:
+                    flowTemp = totalFlows
+                else:
+                    totalFlows = flowTemp
+
                 if not intentsState:
                     # If some intents are not installed, grep the previous flows list, and finished this test case
                     main.log.warn( "Some intens did not install" )
                     main.log.info("Total Intents: {}".format( totalIntents) )
                     break
 
-        totalFlows = main.CLIs[0].getTotalFlowsNum(timeout=600, noExit=True)
         del main.scale[0]
         utilities.assert_equals( expect = main.TRUE,
                                  actual = intentsState,
diff --git a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
index 9822b8b..bfdff59 100644
--- a/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
+++ b/TestON/tests/USECASE/USECASE_SegmentRouting/USECASE_SegmentRouting.py
@@ -286,21 +286,31 @@
             onpass="Flow status is correct!",
             onfail="Flow status is wrong!" )
         main.ONOSbench.dumpFlows( main.ONOSip[0],
-                 main.logdir, "flows" + main.jsonFile)
+                 main.logdir, "flowsBefore" + main.jsonFile)
         #time.sleep( 3*main.startUpSleep)
 
     def CASE4( self, main ):
         main.case( "Check full connectivity" )
         main.log.report( "Check full connectivity" )
 
-        main.step("Check full connectivity")
+        main.step("1st Check full connectivity")
         pa = main.Mininet1.pingall()
         utilities.assert_equals( expect=main.TRUE, actual=pa,
                                  onpass="Full connectivity successfully tested",
                                  onfail="Full connectivity failed" )
         # cleanup mininet
         main.ONOSbench.dumpFlows( main.ONOSip[0],
-                 main.logdir, "flows" + main.jsonFile)
+                 main.logdir, "flowsAfter" + main.jsonFile)
+
+        main.step("2nd Check full connectivity")
+        pa = main.Mininet1.pingall()
+        utilities.assert_equals( expect=main.TRUE, actual=pa,
+                                 onpass="Full connectivity successfully tested",
+                                 onfail="Full connectivity failed" )
+
+        main.ONOSbench.dumpFlows( main.ONOSip[0],
+                 main.logdir, "flowsAfter2nd" + main.jsonFile)
+
         main.ONOSbench.onosStop( main.ONOSip[0] )
         main.Mininet1.stopNet()