blob: 7a2e84f80f112b0649d9d657e2577d2923261c37 [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 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
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':
if self.typeIndex == EventType().NETWORK_LINK_DOWN:
with main.mininetLock:
linkRandom = main.Mininet1.getLinkRandom( switchClasses=r"(OVSSwitch)",
excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ] )
if linkRandom is None:
main.log.warn( "No link available, aborting event" )
return EventStates().ABORT
args[ 0 ] = linkRandom[ 0 ]
args[ 1 ] = linkRandom[ 1 ]
elif self.typeIndex == EventType().NETWORK_LINK_UP:
import random
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
if self.linkA is None or self.linkB is None:
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 is not None and self.linkB is not None:
break
if self.linkA is None or self.linkB is 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 is not None and self.linkB is not 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
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,
END2=self.linkA.deviceB.name,
OPTION="down" )
"""
result = main.Mininet1.delLink( self.linkA.deviceA.name,
self.linkA.deviceB.name )
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 is not None and self.linkB is not 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
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,
END2=self.linkA.deviceB.name,
OPTION="up" )
"""
result = main.Mininet1.addLink( self.linkA.deviceA.name,
self.linkA.deviceB.name )
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:
if main.params[ 'TOPO' ][ 'excludeSwitches' ]:
excludeSwitches = main.params[ 'TOPO' ][ 'excludeSwitches' ].split( ',' )
else:
excludeSwitches = []
with main.mininetLock:
switchRandom = main.Mininet1.getSwitchRandom( switchClasses=r"(OVSSwitch)",
excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ],
excludeSwitches=excludeSwitches )
if switchRandom is None:
main.log.warn( "No switch available, aborting event" )
return EventStates().ABORT
args[ 0 ] = switchRandom
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 ]
if self.device is None:
for device in main.devices:
if device.name == args[ 0 ]:
self.device = device
if self.device is 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 is not None
with main.variableLock:
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 ) )
result = main.TRUE
with main.mininetLock:
# Disable ports toward dual-homed hosts
for host, port in self.device.hosts.items():
if host.isDualHomed:
main.log.info( "Disable port {}/{} which connects to a dual-homed host before bringing down this device".format( self.device.dpid, port ) )
result = result and main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=port, state="disable" )
# result = main.Mininet1.delSwitch( self.device.name )
result = result and main.Mininet1.switch( SW=self.device.name, OPTION="stop" )
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()
for intent in main.intents:
if intent.deviceA == self.device or intent.deviceB == self.device:
intent.setFailed()
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 is not None
with main.variableLock:
if self.device.isUp():
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: ] )
result = main.Mininet1.switch( SW=self.device.name, OPTION='start' )
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()
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:
ips = main.Cluster.getIps()
main.Mininet1.assignSwController( sw=self.device.name, ip=ips )
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
# Bring down again any link that was brought down before the device was down
if int( link.portB ) in link.deviceB.downPorts:
with main.variableLock:
link.bringDown()
link.backwardLink.bringDown()
else:
with main.variableLock:
link.bringUp()
link.backwardLink.bringUp()
# Re-discover hosts
if self.device.hosts:
main.Mininet1.discoverHosts( hostList=[ host.name for host in self.device.hosts ] )
for host in self.device.hosts:
with main.variableLock:
host.bringUp()
self.device.downPorts = []
return EventStates().PASS
class PortEvent( Event ):
def __init__( self ):
Event.__init__( self )
self.device = None
self.port = None
self.link = None
def startPortEvent( self ):
return EventStates().PASS
def startEvent( self, args ):
"""
args are the device name and port number, e.g. [ 's1', '5' ]
"""
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':
if self.typeIndex == EventType().NETWORK_PORT_DOWN:
with main.mininetLock:
linkRandom = main.Mininet1.getLinkRandom( switchClasses=r"(OVSSwitch)",
excludeNodes=[ 'bgp', 'cs', 'nat', 'dhcp', 'r' ])
if linkRandom is None:
main.log.warn( "No link available, aborting event" )
return EventStates().ABORT
for link in main.links:
if link.deviceA.name == linkRandom[ 0 ] and link.deviceB.name == linkRandom[ 1 ]:
self.device = link.deviceA
self.port = int( link.portA )
if not self.device:
main.log.warn( "Failed to get a radnom device port, aborting event" )
return EventStates().ABORT
elif self.typeIndex == EventType().NETWORK_PORT_UP:
import random
with main.variableLock:
downPorts = {}
for link in main.links:
if link.isDown():
if int( link.portA ) in link.deviceA.downPorts:
downPorts[ link.deviceA ] = link.portA
if len( downPorts ) == 0:
main.log.warn( "None of the links are in 'down' state, aborting event" )
return EventStates().ABORT
deviceList = random.sample( downPorts, 1 )
self.device = deviceList[ 0 ]
self.port = int( downPorts[ self.device ] )
if self.device is None:
for device in main.devices:
if device.name == args[ 0 ]:
self.device = device
if self.device is None:
main.log.warn( "Device %s does not exist: " % ( args[ 0 ] ) )
return EventStates().ABORT
if self.port is None:
try:
self.port = int( args[ 1 ] )
except Exception:
main.log.warn( "Device port is not a number: {}".format( args[ 1 ] ) )
return EventStates().ABORT
if self.link is None:
for link in main.links:
if link.deviceA.name == self.device.name and int( link.portA ) == self.port:
self.link = link
if self.link is None:
main.log.warn( "There's no link on device {} port {}".format( self.device.name, self.port ) )
return EventStates().ABORT
main.log.debug( "%s - %s:%s" % ( self.typeString, self.device, self.port ) )
return self.startPortEvent()
class PortDown( PortEvent ):
"""
Generate a port down event giving the device name and port number
"""
def __init__( self ):
PortEvent.__init__( self )
self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
def startPortEvent( self ):
assert self.device is not None and self.port is not None and self.link is not None
if self.link.isDown():
main.log.warn( "port Down - link already down" )
return EventStates().ABORT
elif self.link.isRemoved():
main.log.warn( "port Down - link has been removed" )
return EventStates().ABORT
main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.device.name, self.port ) )
with main.mininetLock:
result = main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=self.port, state="disable" )
if not result:
main.log.warn( "%s - failed to bring down port" % ( self.typeString ) )
return EventStates().FAIL
with main.variableLock:
self.device.downPorts.append( self.port )
self.link.bringDown()
self.link.backwardLink.bringDown()
return EventStates().PASS
class PortUp( PortEvent ):
"""
Generate a port up event giving the device name and port number
"""
def __init__( self ):
PortEvent.__init__( self )
self.typeString = main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeString' ]
self.typeIndex = int( main.params[ 'EVENT' ][ self.__class__.__name__ ][ 'typeIndex' ] )
def startPortEvent( self ):
assert self.device is not None and self.port is not None and self.link is not None
if self.link.isUp():
main.log.warn( "port up - link already up" )
return EventStates().ABORT
elif self.link.isRemoved():
main.log.warn( "port up - link has been removed" )
return EventStates().ABORT
main.log.info( "Event recorded: {} {} {} {}".format( self.typeIndex, self.typeString, self.device.name, self.port ) )
with main.mininetLock:
result = main.Cluster.active( 0 ).CLI.portstate( dpid=self.device.dpid, port=self.port, state="enable" )
if not result:
main.log.warn( "%s - failed to bring up port " % ( self.typeString ) )
return EventStates().FAIL
with main.variableLock:
self.device.downPorts.remove( self.port )
self.link.bringUp()
self.link.backwardLink.bringUp()
return EventStates().PASS