Framework of the new CHOtest

Change-Id: Ie5b58bfa2ed487386443692cbea0d469d7419c24
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
new file mode 100644
index 0000000..7f0ca7c
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
@@ -0,0 +1,246 @@
+"""
+This file contains classes for CHOTestMonkey that are related to application event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.elements.ONOSElement import HostIntent, PointIntent
+
+class IntentEvent( Event ):
+    def __init__( self ):
+        Event.__init__( self )
+        # The index of the ONOS CLI that is going to run the command
+        self.CLIIndex = 0
+
+class HostIntentEvent( IntentEvent ):
+    def __init__( self ):
+        IntentEvent.__init__( self )
+        self.hostA = None
+        self.hostB = None
+
+    def startHostIntentEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if self.typeIndex == EventType().APP_INTENT_HOST_ADD or self.typeIndex == EventType().APP_INTENT_HOST_DEL:
+                if len( args ) < 3:
+                    main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                elif len( args ) > 3:
+                    main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                else:
+                    if args[ 0 ] == args[ 1 ]:
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    for host in main.hosts:
+                        if host.name == args[ 0 ]:
+                            self.hostA = host
+                        elif host.name == args[ 1 ]:
+                            self.hostB = host
+                        if self.hostA != None and self.hostB != None:
+                            break
+                    if self.hostA == None:
+                        main.log.warn( "Host %s does not exist: " % ( args[ 0 ] ) )
+                        return EventStates().ABORT
+                    if self.hostB == None:
+                        main.log.warn( "Host %s does not exist: " % ( args[ 1 ] ) )
+                        return EventStates().ABORT
+                    index = int( args[ 2 ] )
+                    if index < 1 or index > int( main.numCtrls ):
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    if not main.controllers[ index - 1 ].isUp():
+                        main.log.warn( self.typeString + " - invalid argument: onos %s is down" % ( controller.index ) )
+                        return EventStates().ABORT
+                    self.CLIIndex = index
+                    return self.startHostIntentEvent()
+
+class AddHostIntent( HostIntentEvent ):
+    """
+    Add a host-to-host intent (bidirectional)
+    """
+    def __init__( self ):
+        HostIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startHostIntentEvent( self ):
+        assert self.hostA != None and self.hostB != None
+        # Check whether there already exists some intent for the host pair
+        # For now we should avoid installing overlapping intents
+        for intent in main.intents:
+            if not intent.type == 'INTENT_HOST':
+                continue
+            if intent.hostA == self.hostA and intent.hostB == self.hostB or\
+            intent.hostB == self.hostA and intent.hostA == self.hostB:
+                main.log.warn( self.typeString + " - find an exiting intent for the host pair, abort installation" )
+                return EventStates().ABORT
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            id = controller.CLI.addHostIntent( self.hostA.id, self.hostB.id )
+        if id == None:
+            main.log.warn( self.typeString + " - add host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            newHostIntent = HostIntent( id, self.hostA, self.hostB )
+            main.intents.append( newHostIntent )
+            # Update host connectivity status
+            # TODO: should we check whether hostA and hostB are already correspondents?
+            self.hostB.correspondents.append( self.hostA )
+            self.hostA.correspondents.append( self.hostB )
+        return EventStates().PASS
+
+class DelHostIntent( HostIntentEvent ):
+    """
+    Delete a host-to-host intent (bidirectional)
+    """
+    def __init__( self ):
+        HostIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startHostIntentEvent( self ):
+        assert self.hostA != None and self.hostB != None
+        targetIntent = None
+        for intent in main.intents:
+            if not intent.type == 'INTENT_HOST':
+                continue
+            if intent.hostA == self.hostA and intent.hostB == self.hostB or\
+            intent.hostB == self.hostA and intent.hostA == self.hostB:
+                targetIntent = intent
+                break
+        if targetIntent == None:
+            main.log.warn( self.typeString + " - intent does not exist" )
+            return EventStates().FAIL
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.removeIntent( targetIntent.id, purge=True )
+        if result == None or result == main.FALSE:
+            main.log.warn( self.typeString + " - delete host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            main.intents.remove( targetIntent )
+            # Update host connectivity status
+            self.hostB.correspondents.remove( self.hostA )
+            self.hostA.correspondents.remove( self.hostB )
+        return EventStates().PASS
+
+class PointIntentEvent( IntentEvent ):
+    def __init__( self ):
+        IntentEvent.__init__( self )
+        self.deviceA = None
+        self.deviceB = None
+
+    def startPointIntentEvent( self ):
+        return EventStates().PASS
+
+    def startEvent( self, args ):
+        with self.eventLock:
+            main.log.info( "%s - starting event" % ( self.typeString ) )
+            if self.typeIndex == EventType().APP_INTENT_POINT_ADD or self.typeIndex == EventType().APP_INTENT_POINT_DEL:
+                if len( args ) < 3:
+                    main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                elif len( args ) > 3:
+                    main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+                    return EventStates().ABORT
+                else:
+                    for device in main.devices:
+                        if device.name == args[ 0 ]:
+                            self.deviceA = device
+                        elif device.name == args[ 1 ]:
+                            self.deviceB = device
+                        if self.deviceA != None and self.deviceB != None:
+                            break
+                    if self.deviceA == None:
+                        main.log.warn( "Device %s does not exist: " % ( args[ 0 ] ) )
+                        return EventStates().ABORT
+                    if self.deviceB == None:
+                        main.log.warn( "Device %s does not exist: " % ( args[ 1 ] ) )
+                        return EventStates().ABORT
+                    index = int( args[ 2 ] )
+                    if index < 1 or index > int( main.numCtrls ):
+                        main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+                        return EventStates().ABORT
+                    if not main.controllers[ index - 1 ].isUp():
+                        main.log.warn( self.typeString + " - invalid argument: onos %s is down" % ( controller.index ) )
+                        return EventStates().ABORT
+                    self.CLIIndex = index
+                    return self.startPointIntentEvent()
+
+class AddPointIntent( PointIntentEvent ):
+    """
+    Add a point-to-point intent
+    """
+    def __init__( self ):
+        PointIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startPointIntentEvent( self ):
+        assert self.deviceA != None and self.deviceB != None
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        # TODO: the following check only work when we use default port number for point intents
+        # Check whether there already exists some intent for the device pair
+        # For now we should avoid installing overlapping intents
+        for intent in main.intents:
+            if not intent.type == 'INTENT_POINT':
+                continue
+            if intent.deviceA == self.deviceA and intent.deviceB == self.deviceB:
+                main.log.warn( self.typeString + " - find an exiting intent for the device pair, abort installation" )
+                return EventStates().ABORT
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            # TODO: handle the case that multiple hosts attach to one device
+            id = controller.CLI.addPointIntent( self.deviceA.dpid, self.deviceB.dpid,
+                                                1, 1, '',
+                                                self.deviceA.hosts[ 0 ].mac,
+                                                self.deviceB.hosts[ 0 ].mac )
+        if id == None:
+            main.log.warn( self.typeString + " - add point intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            newPointIntent = PointIntent( id, self.deviceA, self.deviceB )
+            main.intents.append( newPointIntent )
+            # Update host connectivity status
+            for hostA in self.deviceA.hosts:
+                for hostB in self.deviceB.hosts:
+                    hostA.correspondents.append( hostB )
+        return EventStates().PASS
+
+class DelPointIntent( PointIntentEvent ):
+    """
+    Delete a point-to-point intent
+    """
+    def __init__( self ):
+        PointIntentEvent.__init__( self )
+        self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+        self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+    def startPointIntentEvent( self ):
+        assert self.deviceA != None and self.deviceB != None
+        targetIntent = None
+        for intent in main.intents:
+            if not intent.type == 'INTENT_POINT':
+                continue
+            if intent.deviceA == self.deviceA and intent.deviceB == self.deviceB:
+                targetIntent = intent
+                break
+        if targetIntent == None:
+            main.log.warn( self.typeString + " - intent does not exist" )
+            return EventStates().FAIL
+        controller = main.controllers[ self.CLIIndex - 1 ]
+        with controller.CLILock:
+            result = controller.CLI.removeIntent( targetIntent.id, purge=True )
+        if result == None or result == main.FALSE:
+            main.log.warn( self.typeString + " - delete host intent failed" )
+            return EventStates().FAIL
+        with main.variableLock:
+            main.intents.remove( targetIntent )
+            # Update host connectivity status
+            for hostA in self.deviceA.hosts:
+                for hostB in self.deviceB.hosts:
+                    hostA.correspondents.remove( hostB )
+        return EventStates().PASS