blob: 7100faee7b2739d3eac7ade9f3e1676e4163e42d [file] [log] [blame]
"""
Copyright 2016 Open Networking Foundation ( ONF )
Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
TestON is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
( at your option ) any later version.
TestON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TestON. If not, see <http://www.gnu.org/licenses/>.
"""
"""
This file contains the event generator class for CHOTestMonkey
Author: you@onlab.us
"""
from threading import Lock, Condition
from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
from tests.CHOTestMonkey.dependencies.EventScheduler import EventScheduleMethod
class MessageType:
def __init__( self ):
self.map = {}
# This message type is used for requesting an event injection from outside CHOTestMonkey
self.EVENT_REQUEST = 1
self.map[ 1 ] = 'EVENT_REQUEST'
# This message tyoe will force the event generator to accept the event injection request for debugging purpose
self.EVENT_REQUEST_DEBUG = 2
self.map[ 2 ] = 'EVENT_REQUEST_DEBUG'
# This message type implies the event generator has inserted the event
self.EVENT_INSERTED = 10
self.map[ 10 ] = 'EVENT_INSERTED'
# This message type implies CHOTestMonkey has refused the event injection request
# due to, e.g. too many pending events in the scheduler
self.EVENT_DENIED = 11
self.map[ 11 ] = 'EVENT_DENIED'
# The followings are error messages
self.UNKNOWN_MESSAGE = 20
self.map[ 20 ] = 'UNKNOWN_MESSAGE'
self.UNKNOWN_EVENT_TYPE = 21
self.map[ 21 ] = 'UNKNOWN_EVENT_TYPE'
self.UNKNOWN_SCHEDULE_METHOD = 22
self.map[ 22 ] = 'UNKNOWN_SCHEDULE_METHOD'
self.NOT_ENOUGH_ARGUMENT = 23
self.map[ 23 ] = 'NOT_ENOUGH_ARGUMENT'
class EventGenerator:
def __init__( self ):
self.default = ''
self.eventGeneratorLock = Lock()
def startListener( self ):
"""
Listen to event triggers
"""
from multiprocessing.connection import Listener
import time
host = "localhost"
port = int( main.params[ 'GENERATOR' ][ 'listenerPort' ] )
address = ( host, port )
listener = Listener( address )
main.log.info( "Event Generator - Event listener start listening on %s:%s" % ( host, port ) )
while True:
conn = listener.accept()
t = main.Thread( target=self.handleConnection,
threadID=main.threadID,
name="handleConnection",
args=[ conn ] )
t.start()
with main.variableLock:
main.threadID += 1
listener.close()
def handleConnection( self, conn ):
"""
Handle connections from event triggers
"""
request = conn.recv()
if isinstance( request, list ) and ( request[ 0 ] == MessageType().EVENT_REQUEST or request[ 0 ] == MessageType().EVENT_REQUEST_DEBUG ):
if len( request ) < 3:
response = MessageType().NOT_ENOUGH_ARGUMENT
elif request[ 0 ] == MessageType().EVENT_REQUEST and not main.eventScheduler.isAvailable():
response = MessageType().EVENT_DENIED
else:
typeString = str( request[ 1 ] )
scheduleMethodString = str( request[ 2 ] )
if len( request ) > 3:
args = request[ 3: ]
else:
args = None
for key, value in EventType().map.items():
if value == typeString:
typeIndex = key
break
if not value == typeString:
response = MessageType().UNKNOWN_EVENT_TYPE
else:
for key, value in EventScheduleMethod().map.items():
if value == scheduleMethodString:
scheduleMethod = key
break
if not value == scheduleMethodString:
response = MessageType().UNKNOWN_SCHEDULE_METHOD
else:
self.insertEvent( typeIndex, scheduleMethod, args )
response = MessageType().EVENT_INSERTED
else:
response = MessageType().UNKNOWN_MESSAGE
conn.send( response )
conn.close()
def triggerEvent( self, typeIndex, scheduleMethod, *args ):
"""
This function triggers an event from inside of CHOTestMonkey
"""
import time
if typeIndex not in EventType().map.keys():
main.log.warn( "Event Generator - Unknown event type: " + str( typeIndex ) )
return
if scheduleMethod not in EventScheduleMethod().map.keys():
main.log.warn( "Event Generator - Unknown event schedule method: " + str( scheduleMethod ) )
return
while not main.eventScheduler.isAvailable():
time.sleep( int( main.params[ 'GENERATOR' ][ 'insertEventRetryInterval' ] ) )
self.insertEvent( typeIndex, scheduleMethod, list( args ) )
def insertEvent( self, typeIndex, scheduleMethod, args=None ):
"""
This function inserts an event into the scheduler
"""
if typeIndex > 100:
# Handle group events
if typeIndex not in main.enabledEvents.keys():
main.log.warn( "Event Generator - event type %s not enabled" % ( typeIndex ) )
return
function = getattr( self, main.enabledEvents[ typeIndex ] )
assert function is not None, "Event Generator - funtion for group event " + typeIndex + " not found"
function( scheduleMethod, args )
else:
# Add individual events to the scheduler
main.eventScheduler.scheduleEvent( typeIndex, scheduleMethod, args )
def insertAllChecks( self, args=None ):
"""
Acquire eventGeneratorLock before calling this funtion
"""
for eventType in main.enabledEvents.keys():
if eventType < 100 and EventType().map[ eventType ].startswith( 'CHECK' ):
main.eventScheduler.scheduleEvent( eventType,
EventScheduleMethod().RUN_NON_BLOCK,
args )
def addAllChecks( self, scheduleMethod, args=None ):
"""
The function adds all check events into the scheduler
"""
with self.eventGeneratorLock:
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
self.insertAllChecks( args )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
def randomLinkToggle( self, scheduleMethod, args=[ 5 ], blocking=True ):
"""
The function randomly adds a link down-up event pair into the scheduler
After each individual link event, all checks are inserted into the scheduler
param:
args[ 0 ] is the average interval between link down and link up events
blocking means blocking other events from being scheduled between link down and link up
"""
import random
import time
if len( args ) < 1:
main.log.warn( "Event Generator - Not enough arguments for randomLinkToggle: %s" % ( args ) )
elif len( args ) > 1:
main.log.warn( "Event Generator - Too many arguments for randomLinkToggle: %s" % ( args ) )
else:
downUpAvgInterval = int( args[ 0 ] )
self.eventGeneratorLock.acquire()
main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
scheduleMethod,
[ 'random', 'random' ] )
sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
# Insert a NULL BLOCK event
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downUpInterval )
main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
scheduleMethod,
[ 'random', 'random' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
self.eventGeneratorLock.release()
def randomLinkGroupToggle( self, scheduleMethod, args=None, blocking=True ):
"""
The function randomly adds a group of link down-up events into the scheduler
After each link down or up, all checks are inserted into the scheduler
param:
args[ 0 ] is the number of links that are to be brought down
args[ 1 ] is the average interval between link down events
args[ 2 ] is the average interval between link group down and group up events
blocking means blocking other events from being scheduled between link events
"""
import random
import time
if len( args ) < 3:
main.log.warn( "Event Generator - Not enough arguments for randomLinkGroupToggle: %s" % ( args ) )
elif len( args ) > 3:
main.log.warn( "Event Generator - Too many arguments for randomLinkGroupToggle: %s" % ( args ) )
else:
linkGroupSize = int( args[ 0 ] )
downDownAvgInterval = int( args[ 1 ] )
downUpAvgInterval = int( args[ 2 ] )
for i in range( 0, linkGroupSize ):
if i == 0:
self.eventGeneratorLock.acquire()
main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_DOWN,
scheduleMethod,
[ 'random', 'random' ] )
sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
# Insert a NULL BLOCK event
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
downDownInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downDownInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downDownInterval )
downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downUpInterval )
for i in range( 0, linkGroupSize ):
main.eventScheduler.scheduleEvent( EventType().NETWORK_LINK_UP,
scheduleMethod,
[ 'random', 'random' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
upUpInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( upUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( upUpInterval )
self.eventGeneratorLock.release()
def randomDeviceToggle( self, scheduleMethod, args=[ 5 ], blocking=True ):
"""
The function randomly removes a device and then adds it back
After each individual device event, all checks are inserted into the scheduler
param:
args[ 0 ] is the average interval between device down and device up events
blocking means blocking other events from being scheduled between device down and device up
"""
import random
import time
if len( args ) < 1:
main.log.warn( "Event Generator - Not enough arguments for randomDeviceToggle: %s" % ( args ) )
elif len( args ) > 1:
main.log.warn( "Event Generator - Too many arguments for randomDeviceToggle: %s" % ( args ) )
else:
downUpAvgInterval = int( args[ 0 ] )
self.eventGeneratorLock.acquire()
main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
scheduleMethod,
[ 'random' ] )
sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkToggle' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
# Insert a NULL BLOCK event
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downUpInterval )
main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
scheduleMethod,
[ 'random' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
self.eventGeneratorLock.release()
def randomDeviceGroupToggle( self, scheduleMethod, args=None, blocking=True ):
"""
The function randomly adds a group of device down-up events into the scheduler
After each device down or up, all checks are inserted into the scheduler
param:
args[ 0 ] is the number of devices that are to be brought down
args[ 1 ] is the average interval between device down events
args[ 2 ] is the average interval between device group down and group up events
blocking means blocking other events from being scheduled between device events
"""
import random
import time
if len( args ) < 3:
main.log.warn( "Event Generator - Not enough arguments for randomDeviceGroupToggle: %s" % ( args ) )
elif len( args ) > 3:
main.log.warn( "Event Generator - Too many arguments for randomDeviceGroupToggle: %s" % ( args ) )
else:
deviceGroupSize = int( args[ 0 ] )
downDownAvgInterval = int( args[ 1 ] )
downUpAvgInterval = int( args[ 2 ] )
for i in range( 0, deviceGroupSize ):
if i == 0:
self.eventGeneratorLock.acquire()
main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_DOWN,
scheduleMethod,
[ 'random' ] )
sleepTime = int( main.params[ 'EVENT' ][ 'randomLinkGroupToggle' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
# Insert a NULL BLOCK event
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
downDownInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downDownInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downDownInterval )
downUpInterval = abs( random.gauss( downUpAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( downUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( downUpInterval )
for i in range( 0, deviceGroupSize ):
main.eventScheduler.scheduleEvent( EventType().NETWORK_DEVICE_UP,
scheduleMethod,
[ 'random' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
upUpInterval = abs( random.gauss( downDownAvgInterval, 1 ) )
if not blocking:
self.eventGeneratorLock.release()
time.sleep( upUpInterval )
self.eventGeneratorLock.acquire()
else:
time.sleep( upUpInterval )
self.eventGeneratorLock.release()
def installAllHostIntents( self, scheduleMethod, args=None ):
"""
This function installs host intents for all host pairs
After all intent events are inserted, this funciton also insert intent and traffic checks
"""
import itertools
with self.eventGeneratorLock:
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
availableControllers = []
for controller in main.controllers:
if controller.isUp():
availableControllers.append( controller.index )
if len( availableControllers ) == 0:
main.log.warn( "Event Generator - No available controllers" )
return
hostCombos = list( itertools.combinations( main.hosts, 2 ) )
for i in xrange( 0, len( hostCombos ), len( availableControllers ) ):
for CLIIndex in availableControllers:
if i >= len( hostCombos ):
break
main.eventScheduler.scheduleEvent( EventType().APP_INTENT_HOST_ADD,
EventScheduleMethod().RUN_NON_BLOCK,
[ hostCombos[ i ][ 0 ].name, hostCombos[ i ][ 1 ].name, CLIIndex ] )
i += 1
# Pending checks after installing all intents
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
sleepTime = int( main.params[ 'EVENT' ][ 'installAllHostIntents' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
def removeAllHostIntents( self, scheduleMethod, args=None ):
"""
This function removes host intents for all host pairs
After all intent events are inserted, this funciton also insert intent and traffic checks
"""
import itertools
with self.eventGeneratorLock:
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
availableControllers = []
for controller in main.controllers:
if controller.isUp():
availableControllers.append( controller.index )
if len( availableControllers ) == 0:
main.log.warn( "Event Generator - No available controllers" )
return
hostCombos = list( itertools.combinations( main.hosts, 2 ) )
for i in xrange( 0, len( hostCombos ), len( availableControllers ) ):
for CLIIndex in availableControllers:
if i >= len( hostCombos ):
break
main.eventScheduler.scheduleEvent( EventType().APP_INTENT_HOST_DEL,
EventScheduleMethod().RUN_NON_BLOCK,
[ hostCombos[ i ][ 0 ].name, hostCombos[ i ][ 1 ].name, CLIIndex ] )
i += 1
# Pending checks after removing all intents
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
sleepTime = int( main.params[ 'EVENT' ][ 'removeAllHostIntents' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
def installAllPointIntents( self, scheduleMethod, args=None ):
"""
This function installs point intents for all device pairs
After all intent events are inserted, this funciton also insert intent and traffic checks
"""
import itertools
with self.eventGeneratorLock:
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
availableControllers = []
for controller in main.controllers:
if controller.isUp():
availableControllers.append( controller.index )
if len( availableControllers ) == 0:
main.log.warn( "Event Generator - No available controllers" )
return
deviceCombos = list( itertools.permutations( main.devices, 2 ) )
for i in xrange( 0, len( deviceCombos ), len( availableControllers ) ):
for CLIIndex in availableControllers:
if i >= len( deviceCombos ):
break
main.eventScheduler.scheduleEvent( EventType().APP_INTENT_POINT_ADD,
EventScheduleMethod().RUN_NON_BLOCK,
[ deviceCombos[ i ][ 0 ].name, deviceCombos[ i ][ 1 ].name, CLIIndex ] )
i += 1
# Pending checks after installing all intents
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
sleepTime = int( main.params[ 'EVENT' ][ 'installAllPointIntents' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
def removeAllPointIntents( self, scheduleMethod, args=None ):
"""
This function removes point intents for all device pairs
After all intent events are inserted, this funciton also insert intent and traffic checks
"""
import itertools
with self.eventGeneratorLock:
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
availableControllers = []
for controller in main.controllers:
if controller.isUp():
availableControllers.append( controller.index )
if len( availableControllers ) == 0:
main.log.warn( "Event Generator - No available controllers" )
return
deviceCombos = list( itertools.permutations( main.devices, 2 ) )
for i in xrange( 0, len( deviceCombos ), len( availableControllers ) ):
for CLIIndex in availableControllers:
if i >= len( deviceCombos ):
break
main.eventScheduler.scheduleEvent( EventType().APP_INTENT_POINT_DEL,
EventScheduleMethod().RUN_NON_BLOCK,
[ deviceCombos[ i ][ 0 ].name, deviceCombos[ i ][ 1 ].name, CLIIndex ] )
i += 1
# Pending checks after removing all intents
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )
sleepTime = int( main.params[ 'EVENT' ][ 'removeAllPointIntents' ][ 'sleepBeforeCheck' ] )
main.eventScheduler.scheduleEvent( EventType().TEST_SLEEP, EventScheduleMethod().RUN_BLOCK, [ sleepTime ] )
self.insertAllChecks( EventScheduleMethod().RUN_NON_BLOCK )
if scheduleMethod == EventScheduleMethod().RUN_BLOCK:
main.eventScheduler.scheduleEvent( EventType().NULL, EventScheduleMethod().RUN_BLOCK )