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
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
new file mode 100644
index 0000000..e48f674
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/CheckEvent.py
@@ -0,0 +1,235 @@
+"""
+This file contains classes for CHOTestMonkey that are related to check event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+
+class CheckEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+
+ def startCheckEvent( self ):
+ return EventStates().PASS
+
+ def startEvent( self, args ):
+ with self.eventLock:
+ main.log.info( "%s - starting event" % ( self.typeString ) )
+ result = self.startCheckEvent()
+ return result
+
+class IntentCheck( CheckEvent ):
+ def __init__( self ):
+ CheckEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCheckEvent( self, args=None ):
+ checkResult = EventStates().PASS
+ intentIDs = []
+ 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 )
+ for controller in main.controllers:
+ if controller.isUp():
+ with controller.CLILock:
+ intentState = controller.CLI.checkIntentState( intentsId=intentIDs )
+ if not intentState:
+ main.log.warn( "Intent Check - Not all intents are in INSTALLED state on ONOS%s" % ( controller.index ) )
+ checkResult = EventStates().FAIL
+ #TODO: check flows?
+ return checkResult
+
+class TopoCheck( CheckEvent ):
+ def __init__( self ):
+ CheckEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCheckEvent( self, args=None ):
+ import json
+ checkResult = EventStates().PASS
+ upLinkNum = 0
+ upDeviceNum = 0
+ upHostNum = 0
+ with main.variableLock:
+ for link in main.links:
+ if not link.isDown() and not link.isRemoved():
+ upLinkNum += 1
+ for device in main.devices:
+ if not device.isDown() and not device.isRemoved():
+ upDeviceNum += 1
+ for host in main.hosts:
+ if not host.isDown() and not host.isRemoved():
+ upHostNum += 1
+ clusterNum = 1
+ for controller in main.controllers:
+ if controller.isUp():
+ with controller.CLILock:
+ topologyOutput = controller.CLI.topology()
+ topoState = controller.CLI.checkStatus( topologyOutput, upDeviceNum, upLinkNum )
+ #if not topoState:
+ # main.log.warn( "Topo Check - link or device number discoverd by ONOS%s is incorrect" % ( controller.index ) )
+ # checkResult = EventStates().FAIL
+ # Check links
+ links = controller.CLI.links()
+ links = json.loads( links )
+ if not len( links ) == upLinkNum:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Topo Check - link number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upLinkNum, len( links ) ) )
+ # Check devices
+ devices = controller.CLI.devices()
+ devices = json.loads( devices )
+ availableDeviceNum = 0
+ for device in devices:
+ if device[ 'available' ] == True:
+ availableDeviceNum += 1
+ if not availableDeviceNum == upDeviceNum:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Topo Check - device number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upDeviceNum, availableDeviceNum ) )
+ # Check hosts
+ hosts = controller.CLI.hosts()
+ hosts = json.loads( hosts )
+ if not len( hosts ) == upHostNum:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Topo Check - host number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, upHostNum, len( hosts ) ) )
+ # Check clusters
+ clusters = controller.CLI.clusters()
+ clusters = json.loads( clusters )
+ if not len( clusters ) == clusterNum:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Topo Check - cluster number discoverd by ONOS%s is incorrect: %s expected and %s actual" % ( controller.index, clusterNum, len( clusters ) ) )
+ return checkResult
+
+class ONOSCheck( CheckEvent ):
+ def __init__( self ):
+ CheckEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCheckEvent( self, args=None ):
+ import json
+ checkResult = EventStates().PASS
+ topics = []
+ # TODO: Other topics?
+ for i in range( 14 ):
+ topics.append( "intent-partition-" + str( i ) )
+ dpidToAvailability = {}
+ dpidToMaster = {}
+ for device in main.devices:
+ if device.isDown() or device.isRemoved():
+ dpidToAvailability[ device.dpid ] = False
+ else:
+ dpidToAvailability[ device.dpid ] = True
+ dpidToMaster[ device.dpid ] = 'unknown'
+ # Check mastership, leaders and node states on each controller node
+ for controller in main.controllers:
+ if controller.isUp():
+ # Check mastership
+ with controller.CLILock:
+ roles = controller.CLI.roles()
+ roles = json.loads( roles )
+ for device in roles:
+ dpid = device[ 'id' ]
+ if dpidToMaster[ dpid ] == 'unknown':
+ 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 ) )
+ 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 ) )
+ # Check leaders
+ with controller.CLILock:
+ leaders = controller.CLI.leaders()
+ leaders = json.loads( leaders )
+ ONOSTopics = [ j['topic'] for j in leaders ]
+ for topic in topics:
+ if topic not in ONOSTopics:
+ checkResult = EventStates().FAIL
+ main.log.warn( "ONOS Check - Topic %s not in leaders on ONOS%s" % ( topic, controller.index ) )
+ # Check node state
+ with controller.CLILock:
+ nodes = controller.CLI.nodes()
+ nodes = json.loads( nodes )
+ ipToState = {}
+ for node in nodes:
+ ipToState[ node[ 'ip' ] ] = node[ 'state' ]
+ for c in main.controllers:
+ if c.isUp() and ipToState[ c.ip ] == 'READY':
+ pass
+ elif not c.isUp() and ipToState[ c.ip ] == 'INACTIVE':
+ pass
+ else:
+ checkResult = EventStates().FAIL
+ main.log.warn( "ONOS Check - ONOS%s shows wrong node state: ONOS%s is %s but state is %s" % ( controller.index, c.index, c.status, ipToState[ c.ip ] ) )
+ # TODO: check partitions?
+ return checkResult
+
+class TrafficCheck( CheckEvent ):
+ def __init__( self ):
+ CheckEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCheckEvent( self, args=None ):
+ checkResult = EventStates().PASS
+ pool = []
+ wait = int( main.params[ 'EVENT' ][ 'TrafficCheck' ][ 'pingWait' ] )
+ timeout = int( main.params[ 'EVENT' ][ 'TrafficCheck' ][ 'pingTimeout' ] )
+ dstIPv4List = {}
+ dstIPv6List = {}
+ upHosts = []
+ for host in main.hosts:
+ if host.isUp():
+ upHosts.append( host )
+ for host in upHosts:
+ dstIPv4List[ host.index ] = []
+ dstIPv6List[ host.index ] = []
+ for correspondent in host.correspondents:
+ if not correspondent in upHosts:
+ continue
+ for ipAddress in correspondent.ipAddresses:
+ if ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv6Prefix' ] ) ):
+ dstIPv6List[ host.index ].append( ipAddress )
+ elif ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv4Prefix' ] ) ):
+ dstIPv4List[ host.index ].append( ipAddress )
+ thread = main.Thread( target=host.handle.pingHostSetAlternative,
+ threadID=main.threadID,
+ name="pingHostSetAlternative",
+ args=[ dstIPv4List[ host.index ], 1 ] )
+ pool.append( thread )
+ thread.start()
+ with main.variableLock:
+ main.threadID += 1
+ for thread in pool:
+ thread.join( 10 )
+ if not thread.result:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Traffic Check - ping failed" )
+
+ if not main.enableIPv6:
+ return checkResult
+ # Check ipv6 ping
+ for host in upHosts:
+ thread = main.Thread( target=host.handle.pingHostSetAlternative,
+ threadID=main.threadID,
+ name="pingHostSetAlternative",
+ args=[ dstIPv6List[ host.index ], 1, True ] )
+ pool.append( thread )
+ thread.start()
+ with main.variableLock:
+ main.threadID += 1
+ for thread in pool:
+ thread.join( 10 )
+ if not thread.result:
+ checkResult = EventStates().FAIL
+ main.log.warn( "Traffic Check - ping6 failed" )
+ return checkResult
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/Event.py b/TestON/tests/CHOTestMonkey/dependencies/events/Event.py
new file mode 100644
index 0000000..2abd77f
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/Event.py
@@ -0,0 +1,43 @@
+"""
+This file contains the Event class for CHOTestMonkey
+Author: you@onlab.us
+"""
+from threading import Lock
+
+class EventType:
+ def __init__( self ):
+ self.map = {}
+ # Group events (>100) should be divided into individual events by the generator before going to the scheduler
+ self.NULL = 0
+ for eventName in main.params[ 'EVENT' ].keys():
+ typeString = main.params[ 'EVENT' ][ eventName ][ 'typeString' ]
+ typeIndex = int( main.params[ 'EVENT' ][ eventName ][ 'typeIndex' ] )
+ setattr( self, typeString, typeIndex )
+ self.map[ typeIndex ] = typeString
+
+class EventStates:
+ def __init__( self ):
+ self.map = {}
+ self.FAIL = 0
+ self.map[ 0 ] = 'FAIL'
+ self.PASS = 1
+ self.map[ 1 ] = 'PASS'
+ self.ABORT = -1
+ self.map[ -1 ] = 'ABORT'
+
+class Event:
+ """
+ Event class for CHOTestMonkey
+ It is the super class for CheckEvent and NetworkEvent
+ """
+ def __init__( self ):
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+ self.eventLock = Lock()
+ self.variableLock = Lock()
+
+ def startEvent( self, args=None ):
+ """
+ Start running the event
+ """
+ return EventStates().PASS
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
new file mode 100644
index 0000000..46b37e7
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/NetworkEvent.py
@@ -0,0 +1,289 @@
+"""
+This file contains classes for CHOTestMonkey that are related to network event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+from tests.CHOTestMonkey.dependencies.elements.NetworkElement import NetworkElement, Device, Host, Link
+from tests.CHOTestMonkey.dependencies.GraphHelper import GraphHelper
+
+class LinkEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+ self.linkA = None
+ self.linkB = None
+
+ def startLinkEvent( self ):
+ return EventStates().PASS
+
+ def startEvent( self, args ):
+ """
+ args are the names of the two link ends, e.g. ['s1', 's2']
+ """
+ with self.eventLock:
+ 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
+ elif len( args ) > 2:
+ main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ if args[ 0 ] == 'random' or args[ 1 ] == 'random':
+ import random
+ if self.typeIndex == EventType().NETWORK_LINK_DOWN:
+ with main.variableLock:
+ graphHelper = GraphHelper()
+ availableLinks = graphHelper.getNonCutEdges()
+ if len( availableLinks ) == 0:
+ main.log.warn( "All links are cut edges, aborting event" )
+ return EventStates().ABORT
+ linkList = random.sample( availableLinks, 1 )
+ self.linkA = linkList[ 0 ]
+ self.linkB = linkList[ 0 ].backwardLink
+ elif self.typeIndex == EventType().NETWORK_LINK_UP:
+ with main.variableLock:
+ downLinks = []
+ for link in main.links:
+ if link.isDown():
+ downLinks.append( link )
+ if len( downLinks ) == 0:
+ main.log.warn( "None of the links are in 'down' state, aborting event" )
+ return EventStates().ABORT
+ linkList = random.sample( downLinks, 1 )
+ self.linkA = linkList[ 0 ]
+ self.linkB = linkList[ 0 ].backwardLink
+ elif args[ 0 ] == args[ 1 ]:
+ main.log.warn( "%s - invalid arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ else:
+ for link in main.links:
+ if link.deviceA.name == args[ 0 ] and link.deviceB.name == args[ 1 ]:
+ self.linkA = link
+ elif link.deviceA.name == args[ 1 ] and link.deviceB.name == args[ 0 ]:
+ self.linkB = link
+ if self.linkA != None and self.linkB != None:
+ break
+ if self.linkA == None or self.linkB == None:
+ main.log.warn( "Bidirectional link %s - %s does not exist: " % ( args[ 0 ], args[ 1 ] ) )
+ return EventStates().ABORT
+ main.log.debug( "%s - %s" % ( self.typeString, self.linkA ) )
+ return self.startLinkEvent()
+
+class LinkDown( LinkEvent ):
+ """
+ Generate a link down event giving the two ends of the link
+ """
+ def __init__( self ):
+ LinkEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startLinkEvent( self ):
+ # TODO: do we need to handle a unidirectional link?
+ assert self.linkA != None and self.linkB != None
+ with main.variableLock:
+ if self.linkA.isDown() or self.linkB.isDown():
+ main.log.warn( "Link Down - link already down" )
+ return EventStates().ABORT
+ elif self.linkA.isRemoved() or self.linkB.isRemoved():
+ main.log.warn( "Link Down - link has been removed" )
+ return EventStates().ABORT
+ with main.mininetLock:
+ result = main.Mininet1.link( END1=self.linkA.deviceA.name,
+ END2=self.linkA.deviceB.name,
+ OPTION="down")
+ if not result:
+ main.log.warn( "%s - failed to bring down link" % ( self.typeString ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ self.linkA.bringDown()
+ self.linkB.bringDown()
+ return EventStates().PASS
+
+class LinkUp( LinkEvent ):
+ """
+ Generate a link up event giving the two ends of the link
+ """
+ def __init__( self ):
+ LinkEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startLinkEvent( self ):
+ assert self.linkA != None and self.linkB != None
+ with main.variableLock:
+ if self.linkA.isUp() or self.linkB.isUp():
+ main.log.warn( "Link Up - link already up" )
+ return EventStates().ABORT
+ if self.linkA.isRemoved() or self.linkB.isRemoved():
+ main.log.warn( "Link Up - link has been removed" )
+ return EventStates().ABORT
+ with main.mininetLock:
+ result = main.Mininet1.link( END1=self.linkA.deviceA.name,
+ END2=self.linkA.deviceB.name,
+ OPTION="up")
+ if not result:
+ main.log.warn( "%s - failed to bring up link" % ( self.typeString ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ self.linkA.bringUp()
+ self.linkB.bringUp()
+ return EventStates().PASS
+
+class DeviceEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+ self.device = None
+
+ def startDeviceEvent( self ):
+ return EventStates().PASS
+
+ def startEvent( self, args ):
+ """
+ args are the names of the device, e.g. 's1'
+ """
+ with self.eventLock:
+ 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
+ elif len( args ) > 1:
+ main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ if args[ 0 ] == 'random':
+ import random
+ if self.typeIndex == EventType().NETWORK_DEVICE_DOWN:
+ with main.variableLock:
+ graphHelper = GraphHelper()
+ availableDevices = graphHelper.getNonCutVertices()
+ if len( availableDevices ) == 0:
+ main.log.warn( "All devices are cut vertices, aborting event" )
+ return EventStates().ABORT
+ deviceList = random.sample( availableDevices, 1 )
+ self.device = deviceList[ 0 ]
+ elif self.typeIndex == EventType().NETWORK_DEVICE_UP:
+ with main.variableLock:
+ removedDevices = []
+ for device in main.devices:
+ if device.isRemoved():
+ removedDevices.append( device )
+ if len( removedDevices ) == 0:
+ main.log.warn( "None of the devices are removed, aborting event" )
+ return EventStates().ABORT
+ deviceList = random.sample( removedDevices, 1 )
+ self.device = deviceList[ 0 ]
+ else:
+ for device in main.devices:
+ if device.name == args[ 0 ]:
+ self.device = device
+ if self.device == None:
+ main.log.warn( "Device %s does not exist: " % ( args[ 0 ] ) )
+ return EventStates().ABORT
+ main.log.debug( "%s - %s" % ( self.typeString, self.device ) )
+ return self.startDeviceEvent()
+
+class DeviceDown( DeviceEvent ):
+ """
+ Generate a device down event (which actually removes this device for now) giving its name
+ """
+ def __init__( self ):
+ DeviceEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startDeviceEvent( self ):
+ assert self.device != None
+ with main.variableLock:
+ if self.device.isRemoved():
+ main.log.warn( "Device Down - device has been removed" )
+ return EventStates().ABORT
+ with main.mininetLock:
+ result = main.Mininet1.delSwitch( self.device.name )
+ if not result:
+ main.log.warn( "%s - failed to bring down device" % ( self.typeString ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ self.device.setRemoved()
+ for link in self.device.outgoingLinks:
+ link.setRemoved()
+ link.backwardLink.setRemoved()
+ for host in self.device.hosts:
+ host.setRemoved()
+ return EventStates().PASS
+
+class DeviceUp( DeviceEvent ):
+ """
+ Generate a device up event (which re-adds this device in case the device is removed) giving its name
+ """
+ def __init__( self ):
+ DeviceEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startDeviceEvent( self ):
+ assert self.device != None
+ with main.variableLock:
+ if self.device.isUp():
+ main.log.warn( "Device Up - device already up" )
+ return EventStates().ABORT
+ # Re-add the device
+ with main.mininetLock:
+ result = main.Mininet1.addSwitch( self.device.name, dpid=self.device.dpid[3:] )
+ if not result:
+ main.log.warn( "%s - failed to re-add device" % ( self.typeString ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ self.device.bringUp()
+ # Re-add links
+ # We add host-device links first since we did the same in mininet topology file
+ # TODO: a more rubust way is to add links according to the port info of the device
+ for host in self.device.hosts:
+ # Add host-device link
+ with main.mininetLock:
+ result = main.Mininet1.addLink( self.device.name, host.name )
+ if not result:
+ main.log.warn( "%s - failed to re-connect host %s to device" % ( self.typeString, host.name ) )
+ return EventStates().FAIL
+ for link in self.device.outgoingLinks:
+ neighbor = link.deviceB
+ # Skip bringing up any link that connecting this device to a removed neighbor
+ if neighbor.isRemoved():
+ continue
+ with main.mininetLock:
+ result = main.Mininet1.addLink( self.device.name, neighbor.name )
+ if not result:
+ main.log.warn( "%s - failed to re-add link to %s" % ( self.typeString, neighbor.name ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ link.bringUp()
+ link.backwardLink.bringUp()
+ # Re-assign mastership for the device
+ with main.mininetLock:
+ main.Mininet1.assignSwController( sw=self.device.name, ip=main.onosIPs )
+ # Re-discover hosts
+ for host in self.device.hosts:
+ correspondent = None
+ for h in main.hosts:
+ if h.isUp() and h != host:
+ correspondent = h
+ break
+ if correspondent == None:
+ with main.mininetLock:
+ main.Mininet1.pingall()
+ if main.enableIPv6:
+ main.Mininet1.pingall( protocol="IPv6" )
+ else:
+ ipv4Addr = None
+ ipv6Addr = None
+ for ipAddress in correspondent.ipAddresses:
+ if ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv6Prefix' ] ) ) and ipv6Addr == None:
+ ipv6Addr = ipAddress
+ elif ipAddress.startswith( str( main.params[ 'TEST' ][ 'ipv4Prefix' ] ) ) and ipv4Addr == None:
+ ipv4Addr = ipAddress
+ assert ipv4Addr != None
+ host.handle.pingHostSetAlternative( [ ipv4Addr ], 1 )
+ if main.enableIPv6:
+ assert ipv6Addr != None
+ host.handle.pingHostSetAlternative( [ ipv6Addr ], 1, True )
+ with main.variableLock:
+ host.bringUp()
+ return EventStates().PASS
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
new file mode 100644
index 0000000..f4e2a89
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/ONOSEvent.py
@@ -0,0 +1,194 @@
+"""
+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
+
+class ONOSEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+ self.ONOSIndex = -1
+
+ def startEvent( self, args ):
+ with self.eventLock:
+ 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:
+ main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+ result = EventStates().ABORT
+ elif len( args ) > 1:
+ main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+ result = EventStates().ABORT
+ else:
+ index = int( args[ 0 ] )
+ if index < 1 or index > int( main.numCtrls ):
+ main.log.warn( "%s - invalid argument: %s" % ( self.typeString, index ) )
+ result = EventStates().ABORT
+ else:
+ self.ONOSIndex = index
+ result = self.startONOSEvent()
+ return result
+
+class ONOSDown( ONOSEvent ):
+ def __init__( self ):
+ ONOSEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startONOSEvent( self ):
+ assert self.ONOSIndex != -1
+ with main.variableLock:
+ if not main.controllers[ self.ONOSIndex - 1 ].isUp():
+ main.log.warn( "ONOS Down - ONOS already down" )
+ return EventStates().ABORT
+ with main.ONOSbenchLock:
+ result = main.ONOSbench.onosStop( main.controllers[ self.ONOSIndex - 1 ].ip )
+ if not result:
+ main.log.warn( "%s - failed to bring down ONOS" % ( self.typeString ) )
+ return EventStates().FAIL
+ with main.variableLock:
+ main.controllers[ self.ONOSIndex - 1 ].bringDown()
+ return EventStates().PASS
+
+class ONOSUp( ONOSEvent ):
+ def __init__( self ):
+ ONOSEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startONOSEvent( self ):
+ assert self.ONOSIndex != -1
+ with main.variableLock:
+ if main.controllers[ self.ONOSIndex - 1 ].isUp():
+ main.log.warn( "ONOS Up - ONOS already up" )
+ return EventStates().ABORT
+ with main.ONOSbenchLock:
+ startResult = main.ONOSbench.onosStart( main.controllers[ self.ONOSIndex - 1 ].ip )
+ if not startResult:
+ main.log.warn( "%s - failed to bring up ONOS" % ( self.typeString ) )
+ return EventStates().FAIL
+ else:
+ ONOSState = main.ONOSbench.isup( main.controllers[ self.ONOSIndex - 1 ].ip )
+ if not ONOSState:
+ main.log.warn( "%s - ONOS is not up" % ( self.typeString ) )
+ return EventStates().FAIL
+ else:
+ cliResult = main.controllers[ self.ONOSIndex - 1 ].startCLI()
+ if not cliResult:
+ main.log.warn( "%s - failed to start ONOS cli" % ( self.typeString ) )
+ return EventStates().FAIL
+ else:
+ with main.variableLock:
+ main.controllers[ self.ONOSIndex - 1 ].bringUp()
+ return EventStates().PASS
+
+class CfgEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+ self.component = ''
+ self.propName = ''
+ self.value = ''
+
+ def startEvent( self, args ):
+ with self.eventLock:
+ main.log.info( "%s - starting event" % ( self.typeString ) )
+ result = self.startCfgEvent( args )
+ return result
+
+class SetCfg( CfgEvent ):
+ def __init__( self ):
+ CfgEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCfgEvent( self, args ):
+ 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:
+ self.component = str( args[ 0 ] )
+ self.propName = str( args[ 1 ] )
+ self.value = str( args[ 2 ] )
+ assert self.component != '' and self.propName != '' and self.value != ''
+ index = -1
+ for controller in main.controllers:
+ if controller.isUp():
+ index = controller.index
+ if index == -1:
+ main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+ return EventStates().ABORT
+ controller = main.controllers[ index - 1 ]
+ with controller.CLILock:
+ result = controller.CLI.setCfg( component=self.component,
+ propName=self.propName,
+ value=self.value )
+ if not result:
+ main.log.warn( "%s - failed to set configuration" % ( self.typeString ) )
+ return EventStates().FAIL
+ return EventStates().PASS
+
+class SetFlowObj( CfgEvent ):
+ def __init__( self ):
+ CfgEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startCfgEvent( self, args ):
+ if len( args ) < 1:
+ main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ elif len( args ) > 1:
+ main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ elif args[ 0 ] != 'true' and args[ 0 ] != 'false':
+ main.log.warn( "%s - Invalid arguments: %s" % ( self.typeString, args ) )
+ return EventStates().ABORT
+ else:
+ self.component = 'org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator'
+ self.propName = 'useFlowObjectives'
+ self.value = str( args[ 0 ] )
+ index = -1
+ for controller in main.controllers:
+ if controller.isUp():
+ index = controller.index
+ if index == -1:
+ main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+ return EventStates().ABORT
+ controller = main.controllers[ index - 1 ]
+ with controller.CLILock:
+ result = controller.CLI.setCfg( component=self.component,
+ propName=self.propName,
+ value=self.value )
+ if not result:
+ main.log.warn( "%s - failed to set configuration" % ( self.typeString ) )
+ return EventStates().FAIL
+ return EventStates().PASS
+
+class BalanceMasters( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex= int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startEvent( self, args=None ):
+ with self.eventLock:
+ main.log.info( "%s - starting event" % ( self.typeString ) )
+ index = -1
+ for controller in main.controllers:
+ if controller.isUp():
+ index = controller.index
+ if index == -1:
+ main.log.warn( "%s - No available controllers" %s ( self.typeString ) )
+ return EventStates().ABORT
+ controller = main.controllers[ index - 1 ]
+ with controller.CLILock:
+ result = controller.CLI.balanceMasters()
+ if not result:
+ main.log.warn( "%s - failed to balance masters" % ( self.typeString ) )
+ return EventStates().FAIL
+ return EventStates().PASS
+
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py b/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py
new file mode 100644
index 0000000..605e43f
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/TestEvent.py
@@ -0,0 +1,59 @@
+"""
+This file contains classes for CHOTestMonkey that are related to check event
+Author: you@onlab.us
+"""
+from tests.CHOTestMonkey.dependencies.events.Event import EventType, EventStates, Event
+
+class TestEvent( Event ):
+ def __init__( self ):
+ Event.__init__( self )
+
+ def startTestEvent( self ):
+ return EventStates().PASS
+
+ def startEvent( self, args ):
+ with self.eventLock:
+ main.log.info( "%s - starting event" % ( self.typeString ) )
+ result = self.startTestEvent( args )
+ return result
+
+class TestPause( TestEvent ):
+ def __init__( self ):
+ TestEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startTestEvent( self, args=None ):
+ result = EventStates().PASS
+ main.eventScheduler.setRunningState( False )
+ return result
+
+class TestResume( TestEvent ):
+ def __init__( self ):
+ TestEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startTestEvent( self, args=None ):
+ result = EventStates().PASS
+ main.eventScheduler.setRunningState( True )
+ return result
+
+class TestSleep( TestEvent ):
+ def __init__( self ):
+ TestEvent.__init__( self )
+ self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
+ self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
+
+ def startTestEvent( self, args ):
+ import time
+ result = EventStates().PASS
+ if len( args ) < 1:
+ main.log.warn( "%s - Not enough arguments: %s" % ( self.typeString, args ) )
+ result = EventStates().ABORT
+ elif len( args ) > 1:
+ main.log.warn( "%s - Too many arguments: %s" % ( self.typeString, args ) )
+ result = EventStates().ABORT
+ sleepTime = int( args[ 0 ] )
+ time.sleep( sleepTime )
+ return result
diff --git a/TestON/tests/CHOTestMonkey/dependencies/events/__init__.py b/TestON/tests/CHOTestMonkey/dependencies/events/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/CHOTestMonkey/dependencies/events/__init__.py