Merge "Record events scheduled in CHOTestMonkey and enable replay from recorded event file"
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
index 0805112..6c9ef4b 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.params
@@ -15,9 +15,10 @@
     # 50. Set FlowObjective to True
     # 51. Set FlowObjective to False
     # 60. Rebalance devices across controllers
-    # 70. Randomly generate events
+    # 70. Run randomly generated events
+    # 80. Replay events from log file
     # 90. Sleep for some time
-    # 100. Do something else
+    # 100. Do nothing
     # Sample sequence: 0,1,2,3,[10,30,21,31,10,32,21,33,50,10,30,21,31,10,32,21,33,51,40,60,10,30,21,31,10,32,21,33,50,10,30,21,31,10,32,21,33,51,41,60]*500,100
     <testcases>
         0,1,2,3,70
@@ -391,6 +392,12 @@
         <deviceDownWeight>2</deviceDownWeight>
     </CASE70>
 
+    <CASE80>
+        <filePath>/home/admin/log-for-replay</filePath>
+        <sleepTime>0.1</sleepTime>
+        <skipChecks>on</skipChecks>
+    </CASE80>
+
     <CASE90>
         <sleepSec>60</sleepSec>
     </CASE90>
diff --git a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
index 42c0ff7..8937a4a 100644
--- a/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
+++ b/TestON/tests/CHOTestMonkey/CHOTestMonkey.py
@@ -794,7 +794,7 @@
                 events.append( 'device-down' )
             for i in range( int( pow( hostIntentNum, 1.5 ) / 100 ) ):
                 events.append( 'del-host-intent' )
-            for i in range( int( pow( pointIntentNum/2, 1.5 ) / 100 ) ):
+            for i in range( int( pow( pointIntentNum, 1.5 ) / 100 ) ):
                 events.append( 'del-point-intent' )
             for i in range( pow( 2, downLinkNum ) - 1 ):
                 events.append( 'link-up' )
@@ -819,13 +819,13 @@
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_ADD, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex, 'bidirectional' )
-                    pointIntentNum += 1
+                    pointIntentNum += 2
             elif event == 'del-point-intent':
-                n = random.randint( 5, pointIntentNum )
+                n = random.randint( 5, pointIntentNum / 2 )
                 for i in range( n ):
                     cliIndex = random.sample( upControllers, 1 )[ 0 ]
                     main.eventGenerator.triggerEvent( EventType().APP_INTENT_POINT_DEL, EventScheduleMethod().RUN_BLOCK, 'random', 'random', cliIndex, 'bidirectional' )
-                    pointIntentNum -= 1
+                    pointIntentNum -= 2
             elif event == 'link-down':
                 main.eventGenerator.triggerEvent( EventType().NETWORK_LINK_DOWN, EventScheduleMethod().RUN_BLOCK, 'random', 'random' )
                 downLinkNum += 1
@@ -855,6 +855,45 @@
                                  onfail="Randomly generate events test failed" )
         time.sleep( main.caseSleep )
 
+    def CASE80( self, main ):
+        """
+        Replay events from log file
+        """
+        import time
+        from tests.CHOTestMonkey.dependencies.events.Event import EventType
+        from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
+
+        main.log.report( "Replay events from log file" )
+        main.log.report( "__________________________________________________" )
+        main.case( "Replay events from log file" )
+        main.step( "Replay events from log file" )
+        main.caseResult = main.TRUE
+        try:
+            f = open( main.params[ 'CASE80' ][ 'filePath' ], 'r' )
+            for line in f.readlines():
+                if 'CHOTestMonkey' in line and 'Event recorded' in line:
+                    line = line.split()
+                    eventIndex = int( line[ 9 ] )
+                    eventName = line[ 10 ]
+                    args = line[ 11: ]
+                    assert eventName.startswith( 'CHECK' )\
+                    or eventName.startswith( 'NETWORK' )\
+                    or eventName.startswith( 'APP' )\
+                    or eventName.startswith( 'ONOS' )
+                    if main.params[ 'CASE80' ][ 'skipChecks' ] == 'on' and eventName.startswith( 'CHECK' ):
+                        continue
+                    with main.eventScheduler.idleCondition:
+                        while not main.eventScheduler.isIdle():
+                            main.eventScheduler.idleCondition.wait()
+                    main.eventGenerator.triggerEvent( eventIndex, EventScheduleMethod().RUN_BLOCK, *args )
+                    time.sleep( float( main.params[ 'CASE80' ][ 'sleepTime' ] ) )
+        except Exception as e:
+            print e
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=main.caseResult,
+                                 onpass="Replay from log file passed",
+                                 onfail="Replay from log file failed" )
+
     def CASE90( self, main ):
         """
         Sleep for some time
diff --git a/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py b/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py
index 0c3e0e0..27b8166 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/EventTrigger.py
@@ -15,7 +15,7 @@
     address = ( host, port )
     conn = Client( address )
     request = []
-    request.append( 2 )
+    request.append( 1 )
     request.append( type )
     request.append( scheduleMethod )
     for arg in args:
@@ -23,7 +23,9 @@
     conn.send( request )
     response = conn.recv()
     while response == 11:
+        conn.close()
         time.sleep( 1 )
+        conn = Client( address )
         conn.send( request )
         response = conn.recv()
     if response == 10:
@@ -64,5 +66,19 @@
         else:
             pass
 
+def replayFromFile( filePath='/home/admin/event-list', sleepTime=1 ):
+    try:
+        f = open( filePath, 'r' )
+        for line in f.readlines():
+            event = line.split()
+            if event[ 3 ].startswith( 'CHECK' ):
+                continue
+            triggerEvent( event[ 3 ], 'RUN_BLOCK', *event[ 4: ] )
+            time.sleep( sleepTime )
+        f.close()
+    except Exception as e:
+        print e
+
 if __name__ == '__main__':
-    testLoop( 2 )
+    #testLoop( 2 )
+    replayFromFile()
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
index 07c8cb6..4283ff6 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/AppEvent.py
@@ -63,7 +63,7 @@
 
     def startEvent( self, args ):
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #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 ) )
@@ -138,6 +138,7 @@
                 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
+            main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.hostA.name, self.hostB.name, self.CLIIndex ) )
             controller = main.controllers[ self.CLIIndex - 1 ]
             with controller.CLILock:
                 id = controller.CLI.addHostIntent( self.hostA.id, self.hostB.id )
@@ -179,6 +180,7 @@
             if targetIntent == None:
                 main.log.warn( self.typeString + " - intent does not exist" )
                 return EventStates().FAIL
+            main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.hostA.name, self.hostB.name, self.CLIIndex ) )
             controller = main.controllers[ self.CLIIndex - 1 ]
             with controller.CLILock:
                 result = controller.CLI.removeIntent( targetIntent.id, purge=True )
@@ -204,7 +206,7 @@
 
     def startEvent( self, args ):
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #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 ) )
@@ -290,6 +292,7 @@
                 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
+            main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.deviceA.name, self.deviceB.name, self.CLIIndex ) )
             controller = main.controllers[ self.CLIIndex - 1 ]
             with controller.CLILock:
                 srcMAC = ""
@@ -336,6 +339,7 @@
             if targetIntent == None:
                 main.log.warn( self.typeString + " - intent does not exist" )
                 return EventStates().FAIL
+            main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.deviceA.name, self.deviceB.name, self.CLIIndex ) )
             controller = main.controllers[ self.CLIIndex - 1 ]
             with controller.CLILock:
                 result = controller.CLI.removeIntent( targetIntent.id, purge=True )
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
index fcbf23d..fa42e12 100755
--- a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -13,7 +13,7 @@
 
     def startEvent( self, args ):
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            main.log.info( "Event recorded: {} {}".format( self.typeIndex, self.typeString ) )
             result = self.startCheckEvent()
             return result
 
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
index 8ec62a1..ccab8a1 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -19,7 +19,7 @@
         args are the names of the two link ends, e.g. ['s1', 's2']
         """
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #main.log.info( "%s - starting event" % ( self.typeString ) )
             if len( args ) < 2:
                 main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
                 return EventStates().ABORT
@@ -84,6 +84,7 @@
             elif self.linkA.isRemoved() or self.linkB.isRemoved():
                 main.log.warn( "Link Down - link has been removed" )
                 return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.linkA.deviceA.name, self.linkA.deviceB.name ) )
         with main.mininetLock:
             '''
             result = main.Mininet1.link( END1=self.linkA.deviceA.name,
@@ -118,6 +119,7 @@
             if self.linkA.isRemoved() or self.linkB.isRemoved():
                 main.log.warn( "Link Up - link has been removed" )
                 return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.linkA.deviceA.name, self.linkA.deviceB.name ) )
         with main.mininetLock:
             '''
             result = main.Mininet1.link( END1=self.linkA.deviceA.name,
@@ -147,7 +149,7 @@
         args are the names of the device, e.g. 's1'
         """
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #main.log.info( "%s - starting event" % ( self.typeString ) )
             if len( args ) < 1:
                 main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
                 return EventStates().ABORT
@@ -199,6 +201,7 @@
             if self.device.isRemoved():
                 main.log.warn( "Device Down - device has been removed" )
                 return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.device.name ) )
         with main.mininetLock:
             result = main.Mininet1.delSwitch( self.device.name )
         if not result:
@@ -232,6 +235,7 @@
                 main.log.warn( "Device Up - device already up" )
                 return EventStates().ABORT
         # Re-add the device
+        main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.device.name ) )
         with main.mininetLock:
             result = main.Mininet1.addSwitch( self.device.name, dpid=self.device.dpid[3:] )
         if not result:
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
index f4e2a89..28960d2 100644
--- a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
@@ -11,7 +11,7 @@
 
     def startEvent( self, args ):
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #main.log.info( "%s - starting event" % ( self.typeString ) )
             result = EventStates().PASS
             if self.typeIndex == EventType().ONOS_ONOS_DOWN or self.typeIndex == EventType().ONOS_ONOS_UP:
                 if len( args ) < 1:
@@ -42,6 +42,7 @@
             if not main.controllers[ self.ONOSIndex - 1 ].isUp():
                 main.log.warn( "ONOS Down - ONOS already down" )
                 return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.ONOSIndex ) )
         with main.ONOSbenchLock:
             result = main.ONOSbench.onosStop( main.controllers[ self.ONOSIndex - 1 ].ip )
         if not result:
@@ -63,6 +64,7 @@
             if main.controllers[ self.ONOSIndex - 1 ].isUp():
                 main.log.warn( "ONOS Up - ONOS already up" )
                 return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {}".format( self.typeIndex, self.typeString, self.ONOSIndex ) )
         with main.ONOSbenchLock:
             startResult = main.ONOSbench.onosStart( main.controllers[ self.ONOSIndex - 1 ].ip )
         if not startResult:
@@ -92,7 +94,7 @@
 
     def startEvent( self, args ):
         with self.eventLock:
-            main.log.info( "%s - starting event" % ( self.typeString ) )
+            #main.log.info( "%s - starting event" % ( self.typeString ) )
             result = self.startCfgEvent( args )
             return result
 
@@ -121,6 +123,7 @@
         if index == -1:
             main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
             return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.component, self.propName, self.value ) )
         controller = main.controllers[ index - 1 ]
         with controller.CLILock:
             result = controller.CLI.setCfg( component=self.component,
@@ -158,6 +161,7 @@
         if index == -1:
             main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
             return EventStates().ABORT
+        main.log.info( "Event recorded: {} {} {} {} {}".format( self.typeIndex, self.typeString, self.component, self.propName, self.value ) )
         controller = main.controllers[ index - 1 ]
         with controller.CLILock:
             result = controller.CLI.setCfg( component=self.component,
@@ -184,6 +188,7 @@
             if index == -1:
                 main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
                 return EventStates().ABORT
+            main.log.info( "Event recorded: {} {}".format( self.typeIndex, self.typeString ) )
             controller = main.controllers[ index - 1 ]
             with controller.CLILock:
                 result = controller.CLI.balanceMasters()