[ONOS-7915] Run CHO test on Flex POD
Change-Id: Id8e59e044361a7781bed39f0878df97c2a67af8f
(cherry picked from commit c61aaa231a325558580fc55b774408a91a4dc0c1)
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 5318ffb..661ada7 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -1935,7 +1935,7 @@
main.log.exception( self.name + ": Uncaught exception!" )
main.cleanAndExit()
- def getSwitchRandom( self, timeout=60, nonCut=True, switchClasses=None, excludeNodes=[], excludeSwitches=[] ):
+ def getSwitchRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipSwitches=[] ):
"""
Randomly get a switch from Mininet topology.
If nonCut is True, it gets a list of non-cut switches (the deletion
@@ -1944,26 +1944,25 @@
it just randomly returns one switch from all current switches in
Mininet.
excludeNodes will be pased to getGraphDict method
- Switches specified in excludeSwitches will be excluded
+ Switches specified in skipSwitches will be excluded
Returns the name of the chosen switch.
"""
import random
candidateSwitches = []
try:
if not nonCut:
- switches = self.getSwitches( timeout=timeout, switchClasses=switchClasses )
+ switches = self.getSwitches( timeout=timeout, excludeNodes=excludeNodes )
assert len( switches ) != 0
for switchName in switches.keys():
candidateSwitches.append( switchName )
else:
graphDict = self.getGraphDict( timeout=timeout, useId=False,
- switchClasses=switchClasses,
excludeNodes=excludeNodes )
if graphDict is None:
return None
self.graph.update( graphDict )
candidateSwitches = self.graph.getNonCutVertices()
- candidateSwitches = [ switch for switch in candidateSwitches if switch not in excludeSwitches ]
+ candidateSwitches = [ switch for switch in candidateSwitches if switch not in skipSwitches ]
if candidateSwitches is None:
return None
elif len( candidateSwitches ) == 0:
@@ -2076,7 +2075,7 @@
main.log.exception( self.name + ": Uncaught exception!" )
main.cleanAndExit()
- def getLinkRandom( self, timeout=60, nonCut=True, switchClasses=None, excludeNodes=[] ):
+ def getLinkRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipLinks=[] ):
"""
Randomly get a link from Mininet topology.
If nonCut is True, it gets a list of non-cut links (the deletion
@@ -2085,6 +2084,7 @@
it just randomly returns one link from all current links in
Mininet.
excludeNodes will be passed to getLinks method to exclude unexpected links.
+ Any link that has either end included in skipLinks will be excluded
Returns the link as a list, e.g. [ 's1', 's2' ]
"""
import random
@@ -2100,12 +2100,13 @@
candidateLinks.append( [ link[ 'node1' ], link[ 'node2' ] ] )
else:
graphDict = self.getGraphDict( timeout=timeout, useId=False,
- switchClasses=switchClasses,
excludeNodes=excludeNodes )
if graphDict is None:
return None
self.graph.update( graphDict )
candidateLinks = self.graph.getNonCutEdges()
+ candidateLinks = [ link for link in candidateLinks
+ if link[0] not in skipLinks and link[1] not in skipLinks ]
if candidateLinks is None:
return None
elif len( candidateLinks ) == 0:
@@ -2821,12 +2822,14 @@
main.log.exception( self.name + ": Uncaught exception!" )
main.cleanAndExit()
- def getSwitches( self, verbose=False, updateTimeout=1000, switchClasses=None ):
+ def getSwitches( self, verbose=False, updateTimeout=1000, excludeNodes=[] ):
"""
Read switches from Mininet.
Returns a dictionary whose keys are the switch names and the value is
a dictionary containing information about the switch.
+ If excludeNodes is specified, switches with names included in excludeNodes
+ will be ingored.
"""
# NOTE: To support new Mininet switch classes, just append the new
# class to the switchClasses variable
@@ -2838,8 +2841,7 @@
# <OVSSwitchNS s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None,s1-eth3:None pid=22550>
# <OVSBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=26830>
# <UserSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=14737>
- if not switchClasses:
- switchClasses = r"(OVSSwitch)|(OVSBridge)|(OVSSwitchNS)|(IVSSwitch)|(LinuxBridge)|(UserSwitch)"
+ switchClasses = r"(OVSSwitch)|(OVSBridge)|(OVSSwitchNS)|(IVSSwitch)|(LinuxBridge)|(UserSwitch)"
try:
swRE = r"<(?P<class>" + switchClasses + r")" +\
r"(?P<options>\{.*\})?\s" +\
@@ -2854,6 +2856,8 @@
result = re.search( swRE, line, re.I )
if result:
name = result.group( 'name' )
+ if name in excludeNodes:
+ continue
dpid = str( self.getSwitchDPID( name ) ).zfill( 16 )
pid = result.group( 'pid' )
swClass = result.group( 'class' )
@@ -2967,8 +2971,8 @@
'port1': str( port1 of_port )
'port2': str( port2 of_port ) }
- If either node1 or node2 name starts with any of the strings sepcified
- in excludeNodes, the link will be excluded from the returned value
+ If either node1 or node2 name matches any of the names sepcified in
+ excludeNodes, the link will be excluded from the returned value
Note: The port number returned is the eth#, not necessarily the of_port
number. In Mininet, for OVS switch, these should be the same. For
@@ -2989,8 +2993,7 @@
if match:
node1 = match.group( 'node1' )
node2 = match.group( 'node2' )
- if any( node1.startswith( node ) for node in excludeNodes ) or \
- any( node2.startswith( node ) for node in excludeNodes ):
+ if any( node1 == node or node2 == node for node in excludeNodes ):
continue
port1 = match.group( 'port1' )
port2 = match.group( 'port2' )
@@ -3461,7 +3464,7 @@
main.cleanAndExit()
def getGraphDict( self, timeout=60, useId=True, includeHost=False,
- switchClasses=None, excludeNodes=[] ):
+ excludeNodes=[] ):
"""
Return a dictionary which describes the latest Mininet topology data as a
graph.
@@ -3480,9 +3483,8 @@
topology.
If includeHost == True, all hosts (and host-switch links) will be included
in topology data.
- if switchClasses == None, default switchClasses will be used when calling
- getSwitches method.
- excludeNodes will be passed to getLinks method to exclude unexpected links.
+ excludeNodes will be passed to getSwitches and getLinks methods to exclude
+ unexpected switches and links.
Note that link or switch that are brought down by 'link x x down' or 'switch
x down' commands still show in the output of Mininet CLI commands such as
'links', 'dump', etc. Thus, to ensure the correctness of this function, it is
@@ -3493,11 +3495,11 @@
try:
links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
portDict = {}
- switches = self.getSwitches( switchClasses=switchClasses )
+ switches = self.getSwitches( excludeNodes=excludeNodes )
if includeHost:
hosts = self.getHosts()
for link in links:
- # FIXME: support 'includeHost' argument
+ # TODO: support 'includeHost' argument
if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
continue
nodeName1 = link[ 'node1' ]
@@ -3549,7 +3551,7 @@
for port in switches[ nodeName1 ][ 'ports' ]:
if port[ 'of_port' ] == str( portIndex ):
# Use -1 as index for disabled port
- if port[ 'enabled' ] == True:
+ if port[ 'enabled' ]:
graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': portIndex }
else:
graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': -1 }
diff --git a/TestON/drivers/common/cli/hostdriver.py b/TestON/drivers/common/cli/hostdriver.py
index d8cc74d..7c7cd80 100644
--- a/TestON/drivers/common/cli/hostdriver.py
+++ b/TestON/drivers/common/cli/hostdriver.py
@@ -38,6 +38,7 @@
self.handle = self
self.name = None
self.shortName = None
+ self.interfaces = []
self.home = None
self.inband = False
self.prompt = "\$"
@@ -56,6 +57,10 @@
vars( self )[ key ] = connectargs[ key ]
self.name = self.options[ 'name' ]
self.shortName = self.options[ 'shortName' ]
+ self.interfaces.append( { 'ips': [ self.options[ 'ip' ] ],
+ 'isUp': True,
+ 'mac': self.options[ 'mac' ],
+ 'name': None } )
try:
if os.getenv( str( self.ip_address ) ) is not None:
@@ -109,13 +114,26 @@
try:
if self.handle:
# Disconnect from the host
- self.handle.sendline( "" )
- self.handle.expect( self.prompt )
- self.handle.sendline( "exit" )
- i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
- if i == 1:
- main.log.error( self.name + ": timeout when waiting for response" )
- main.log.error( "response: " + str( self.handle.before ) )
+ if not self.options[ 'inband' ] == 'True':
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "exit" )
+ i = self.handle.expect( [ "closed", pexpect.TIMEOUT ] )
+ if i == 1:
+ main.log.error( self.name + ": timeout when waiting for response" )
+ main.log.error( "response: " + str( self.handle.before ) )
+ else:
+ self.handle.sendline( "" )
+ i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=2 )
+ if i == 1:
+ main.log.warn( self.name + ": timeout when waiting for response" )
+ main.log.warn( "response: " + str( self.handle.before ) )
+ self.handle.sendline( "exit" )
+ i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
+ if i == 1:
+ main.log.warn( self.name + ": timeout when waiting for response" )
+ main.log.warn( "response: " + str( self.handle.before ) )
+ return main.TRUE
except TypeError:
main.log.exception( self.name + ": Object not as expected" )
response = main.FALSE
@@ -178,7 +196,7 @@
main.log.info( "Skip disconnecting the host via data plane" )
return main.TRUE
self.handle.sendline( "" )
- self.handle.expect( self.prompt )
+ self.handle.expect( self.prompt, timeout=2 )
self.handle.sendline( "exit" )
i = self.handle.expect( [ "closed", pexpect.TIMEOUT ], timeout=2 )
if i == 1:
diff --git a/TestON/drivers/common/cli/networkdriver.py b/TestON/drivers/common/cli/networkdriver.py
index 828187c..1cd242c 100755
--- a/TestON/drivers/common/cli/networkdriver.py
+++ b/TestON/drivers/common/cli/networkdriver.py
@@ -32,6 +32,7 @@
import re
import types
from drivers.common.clidriver import CLI
+from core.graph import Graph
class NetworkDriver( CLI ):
@@ -45,7 +46,9 @@
self.handle = None
self.switches = {}
self.hosts = {}
+ self.links = {}
super( NetworkDriver, self ).__init__()
+ self.graph = Graph()
def checkOptions( self, var, defaultVar ):
if var is None or var == "":
@@ -261,17 +264,81 @@
main.log.error( self.name + ": failed to disconnect inband hosts" )
return main.FALSE
- def getSwitches( self ):
+ def getSwitches( self, timeout=60, excludeNodes=[], includeStopped=False ):
"""
- Return a dictionary which maps short names to switch component
+ Return a dictionary which maps short names to switch data
+ If includeStopped is True, stopped switches will also be included
"""
- return self.switches
+ switches = {}
+ for switchName, switchComponent in self.switches.items():
+ if switchName in excludeNodes:
+ continue
+ if not includeStopped and not switchComponent.isup:
+ continue
+ dpid = switchComponent.dpid.replace( '0x', '' ).zfill( 16 )
+ ports = switchComponent.ports
+ swClass = 'Unknown'
+ pid = None
+ options = None
+ switches[ switchName ] = { "dpid": dpid,
+ "ports": ports,
+ "swClass": swClass,
+ "pid": pid,
+ "options": options }
+ return switches
- def getHosts( self ):
+ def getHosts( self, hostClass=None ):
"""
- Return a dictionary which maps short names to host component
+ Return a dictionary which maps short names to host data
"""
- return self.hosts
+ hosts = {}
+ for hostName, hostComponent in self.hosts.items():
+ interfaces = hostComponent.interfaces
+ hosts[ hostName ] = { "interfaces": interfaces }
+ return hosts
+
+ def updateLinks( self, timeout=60, excludeNodes=[] ):
+ """
+ Update self.links by getting up-to-date port information from
+ switches
+ """
+ # TODO: also inlcude switch-to-host links
+ self.links = {}
+ for node1 in self.switches.keys():
+ if node1 in excludeNodes:
+ continue
+ self.links[ node1 ] = {}
+ self.switches[ node1 ].updatePorts()
+ for port in self.switches[ node1 ].ports:
+ if not port[ 'enabled' ]:
+ continue
+ node2 = getattr( main, port[ 'node2' ] ).shortName
+ if node2 in excludeNodes:
+ continue
+ port1 = port[ 'of_port' ]
+ port2 = port[ 'port2' ]
+ if not self.links[ node1 ].get( node2 ):
+ self.links[ node1 ][ node2 ] = {}
+ # Check if this link already exists
+ if self.links.get( node2 ):
+ if self.links[ node2 ].get( node1 ):
+ if self.links[ node2 ].get( node1 ).get( port2 ):
+ assert self.links[ node2 ][ node1 ][ port2 ] == port1
+ continue
+ self.links[ node1 ][ node2 ][ port1 ] = port2
+
+ def getLinks( self, timeout=60, excludeNodes=[] ):
+ """
+ Return a list of links specify both node names and port numbers
+ """
+ self.updateLinks( timeout=timeout, excludeNodes=excludeNodes )
+ links = []
+ for node1, nodeLinks in self.links.items():
+ for node2, ports in nodeLinks.items():
+ for port1, port2 in ports.items():
+ links.append( { 'node1': node1, 'node2': node2,
+ 'port1': port1, 'port2': port2 } )
+ return links
def getMacAddress( self, host ):
"""
@@ -279,8 +346,7 @@
"""
import re
try:
- hosts = self.getHosts()
- hostComponent = hosts[ host ]
+ hostComponent = self.hosts[ host ]
response = hostComponent.ifconfig()
pattern = r'HWaddr\s([0-9A-F]{2}[:-]){5}([0-9A-F]{2})'
macAddressSearch = re.search( pattern, response, re.I )
@@ -297,8 +363,7 @@
hostName: name of the host e.g. "h1"
cmd: command to run on the host
"""
- hosts = self.getHosts()
- hostComponent = hosts[ hostName ]
+ hostComponent = self.hosts[ hostName ]
if hostComponent:
return hostComponent.command( cmd )
return None
@@ -404,8 +469,7 @@
returnValue = main.TRUE
ipv6 = True if protocol == "IPv6" else False
startTime = time.time()
- hosts = self.getHosts()
- hostPairs = itertools.permutations( list( hosts.values() ), 2 )
+ hostPairs = itertools.permutations( list( self.hosts.values() ), 2 )
for hostPair in list( hostPairs ):
ipDst = hostPair[ 1 ].options[ 'ip6' ] if ipv6 else hostPair[ 1 ].options[ 'ip' ]
pingResult = hostPair[ 0 ].ping( ipDst, ipv6=ipv6 )
@@ -446,9 +510,8 @@
import time
import itertools
hostComponentList = []
- hosts = self.getHosts()
for hostName in hostList:
- hostComponent = hosts[ hostName ]
+ hostComponent = self.hosts[ hostName ]
if hostComponent:
hostComponentList.append( hostComponent )
try:
@@ -504,10 +567,9 @@
main.FALSE otherwise
"""
try:
- hosts = self.getHosts()
if not hostList:
- hostList = hosts.keys()
- for hostName, hostComponent in hosts.items():
+ hostList = self.hosts.keys()
+ for hostName, hostComponent in self.hosts.items():
if hostName not in hostList:
continue
ipList = []
@@ -528,7 +590,7 @@
hostList.remove( hostName )
return main.FALSE if hostList else main.TRUE
except KeyError:
- main.log.exception( self.name + ": host data not as expected: " + hosts )
+ main.log.exception( self.name + ": host data not as expected: " + self.hosts.keys() )
return None
except pexpect.EOF:
main.log.error( self.name + ": EOF exception found" )
@@ -584,3 +646,272 @@
" is " +
ipAddressSearch.group( 1 ) )
return ipAddressSearch.group( 1 )
+
+ def getLinkRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipLinks=[] ):
+ """
+ Randomly get a link from network topology.
+ If nonCut is True, it gets a list of non-cut links (the deletion
+ of a non-cut link will not increase the number of connected
+ component of a graph) and randomly returns one of them, otherwise
+ it just randomly returns one link from all current links.
+ excludeNodes will be passed to getLinks and getGraphDict method.
+ Any link that has either end included in skipLinks will be excluded.
+ Returns the link as a list, e.g. [ 's1', 's2' ].
+ """
+ import random
+ candidateLinks = []
+ try:
+ if not nonCut:
+ links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
+ assert len( links ) != 0
+ for link in links:
+ # Exclude host-switch link
+ if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
+ continue
+ candidateLinks.append( [ link[ 'node1' ], link[ 'node2' ] ] )
+ else:
+ graphDict = self.getGraphDict( timeout=timeout, useId=False,
+ excludeNodes=excludeNodes )
+ if graphDict is None:
+ return None
+ self.graph.update( graphDict )
+ candidateLinks = self.graph.getNonCutEdges()
+ candidateLinks = [ link for link in candidateLinks
+ if link[0] not in skipLinks and link[1] not in skipLinks ]
+ if candidateLinks is None:
+ return None
+ elif len( candidateLinks ) == 0:
+ main.log.info( self.name + ": No candidate link for deletion" )
+ return None
+ else:
+ link = random.sample( candidateLinks, 1 )
+ return link[ 0 ]
+ except KeyError:
+ main.log.exception( self.name + ": KeyError exception found" )
+ return None
+ except AssertionError:
+ main.log.exception( self.name + ": AssertionError exception found" )
+ return None
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception" )
+ return None
+
+ def getSwitchRandom( self, timeout=60, nonCut=True, excludeNodes=[], skipSwitches=[] ):
+ """
+ Randomly get a switch from network topology.
+ If nonCut is True, it gets a list of non-cut switches (the deletion
+ of a non-cut switch will not increase the number of connected
+ components of a graph) and randomly returns one of them, otherwise
+ it just randomly returns one switch from all current switches in
+ Mininet.
+ excludeNodes will be pased to getSwitches and getGraphDict method.
+ Switches specified in skipSwitches will be excluded.
+ Returns the name of the chosen switch.
+ """
+ import random
+ candidateSwitches = []
+ try:
+ if not nonCut:
+ switches = self.getSwitches( timeout=timeout, excludeNodes=excludeNodes )
+ assert len( switches ) != 0
+ for switchName in switches.keys():
+ candidateSwitches.append( switchName )
+ else:
+ graphDict = self.getGraphDict( timeout=timeout, useId=False,
+ excludeNodes=excludeNodes )
+ if graphDict is None:
+ return None
+ self.graph.update( graphDict )
+ candidateSwitches = self.graph.getNonCutVertices()
+ candidateSwitches = [ switch for switch in candidateSwitches if switch not in skipSwitches ]
+ if candidateSwitches is None:
+ return None
+ elif len( candidateSwitches ) == 0:
+ main.log.info( self.name + ": No candidate switch for deletion" )
+ return None
+ else:
+ switch = random.sample( candidateSwitches, 1 )
+ return switch[ 0 ]
+ except KeyError:
+ main.log.exception( self.name + ": KeyError exception found" )
+ return None
+ except AssertionError:
+ main.log.exception( self.name + ": AssertionError exception found" )
+ return None
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception" )
+ return None
+
+ def getGraphDict( self, timeout=60, useId=True, includeHost=False,
+ excludeNodes=[] ):
+ """
+ Return a dictionary which describes the latest network topology data as a
+ graph.
+ An example of the dictionary:
+ { vertex1: { 'edges': ..., 'name': ..., 'protocol': ... },
+ vertex2: { 'edges': ..., 'name': ..., 'protocol': ... } }
+ Each vertex should at least have an 'edges' attribute which describes the
+ adjacency information. The value of 'edges' attribute is also represented by
+ a dictionary, which maps each edge (identified by the neighbor vertex) to a
+ list of attributes.
+ An example of the edges dictionary:
+ 'edges': { vertex2: { 'port': ..., 'weight': ... },
+ vertex3: { 'port': ..., 'weight': ... } }
+ If useId == True, dpid/mac will be used instead of names to identify
+ vertices, which is helpful when e.g. comparing network topology with ONOS
+ topology.
+ If includeHost == True, all hosts (and host-switch links) will be included
+ in topology data.
+ excludeNodes will be passed to getSwitches and getLinks methods to exclude
+ unexpected switches and links.
+ """
+ # TODO: support excludeNodes
+ graphDict = {}
+ try:
+ links = self.getLinks( timeout=timeout, excludeNodes=excludeNodes )
+ portDict = {}
+ switches = self.getSwitches( excludeNodes=excludeNodes )
+ if includeHost:
+ hosts = self.getHosts()
+ for link in links:
+ # TODO: support 'includeHost' argument
+ if link[ 'node1' ].startswith( 'h' ) or link[ 'node2' ].startswith( 'h' ):
+ continue
+ nodeName1 = link[ 'node1' ]
+ nodeName2 = link[ 'node2' ]
+ if not self.switches[ nodeName1 ].isup or not self.switches[ nodeName2 ].isup:
+ continue
+ port1 = link[ 'port1' ]
+ port2 = link[ 'port2' ]
+ # Loop for two nodes
+ for i in range( 2 ):
+ portIndex = port1
+ if useId:
+ node1 = 'of:' + str( switches[ nodeName1 ][ 'dpid' ] )
+ node2 = 'of:' + str( switches[ nodeName2 ][ 'dpid' ] )
+ else:
+ node1 = nodeName1
+ node2 = nodeName2
+ if node1 not in graphDict.keys():
+ if useId:
+ graphDict[ node1 ] = { 'edges': {},
+ 'dpid': switches[ nodeName1 ][ 'dpid' ],
+ 'name': nodeName1,
+ 'ports': switches[ nodeName1 ][ 'ports' ],
+ 'swClass': switches[ nodeName1 ][ 'swClass' ],
+ 'pid': switches[ nodeName1 ][ 'pid' ],
+ 'options': switches[ nodeName1 ][ 'options' ] }
+ else:
+ graphDict[ node1 ] = { 'edges': {} }
+ else:
+ # Assert node2 is not connected to any current links of node1
+ # assert node2 not in graphDict[ node1 ][ 'edges' ].keys()
+ pass
+ for port in switches[ nodeName1 ][ 'ports' ]:
+ if port[ 'of_port' ] == str( portIndex ):
+ # Use -1 as index for disabled port
+ if port[ 'enabled' ]:
+ graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': portIndex }
+ else:
+ graphDict[ node1 ][ 'edges' ][ node2 ] = { 'port': -1 }
+ # Swap two nodes/ports
+ nodeName1, nodeName2 = nodeName2, nodeName1
+ port1, port2 = port2, port1
+ # Remove links with disabled ports
+ linksToRemove = []
+ for node, edges in graphDict.items():
+ for neighbor, port in edges[ 'edges' ].items():
+ if port[ 'port' ] == -1:
+ linksToRemove.append( ( node, neighbor ) )
+ for node1, node2 in linksToRemove:
+ for i in range( 2 ):
+ if graphDict.get( node1 )[ 'edges' ].get( node2 ):
+ graphDict[ node1 ][ 'edges' ].pop( node2 )
+ node1, node2 = node2, node1
+ return graphDict
+ except KeyError:
+ main.log.exception( self.name + ": KeyError exception found" )
+ return None
+ except AssertionError:
+ main.log.exception( self.name + ": AssertionError exception found" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception" )
+ return None
+
+ def switch( self, **switchargs ):
+ """
+ start/stop a switch
+ """
+ args = utilities.parse_args( [ "SW", "OPTION" ], **switchargs )
+ sw = args[ "SW" ] if args[ "SW" ] is not None else ""
+ option = args[ "OPTION" ] if args[ "OPTION" ] is not None else ""
+ try:
+ switchComponent = self.switches[ sw ]
+ if option == 'stop':
+ switchComponent.stopOfAgent()
+ elif option == 'start':
+ switchComponent.startOfAgent()
+ else:
+ main.log.warn( self.name + ": Unknown switch command" )
+ return main.FALSE
+ return main.TRUE
+ except KeyError:
+ main.log.error( self.name + ": Not able to find switch [}".format( sw ) )
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": TIMEOUT exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception" )
+ main.cleanAndExit()
+
+ def discoverHosts( self, hostList=[], wait=1000, dstIp="6.6.6.6", dstIp6="1020::3fe" ):
+ '''
+ Hosts in hostList will do a single ARP/ND to a non-existent address for ONOS to
+ discover them. A host will use arping/ndisc6 to send ARP/ND depending on if it
+ has IPv4/IPv6 addresses configured.
+ Optional:
+ hostList: a list of names of the hosts that need to be discovered. If not
+ specified mininet will send ping from all the hosts
+ wait: timeout for ARP/ND in milliseconds
+ dstIp: destination address used by IPv4 hosts
+ dstIp6: destination address used by IPv6 hosts
+ Returns:
+ main.TRUE if all packets were successfully sent. Otherwise main.FALSE
+ '''
+ try:
+ hosts = self.getHosts()
+ if not hostList:
+ hostList = hosts.keys()
+ discoveryResult = main.TRUE
+ for host in hostList:
+ flushCmd = ""
+ cmd = ""
+ if self.getIPAddress( host ):
+ flushCmd = "sudo ip neigh flush all"
+ cmd = "arping -c 1 -w {} {}".format( wait, dstIp )
+ main.log.debug( "Sending IPv4 arping from host {}".format( host ) )
+ elif self.getIPAddress( host, proto='IPV6' ):
+ flushCmd = "sudo ip -6 neigh flush all"
+ intf = hosts[host]['interfaces'][0]['name']
+ cmd = "ndisc6 -r 1 -w {} {} {}".format( wait, dstIp6, intf )
+ main.log.debug( "Sending IPv6 ND from host {}".format( host ) )
+ else:
+ main.log.warn( "No IP addresses configured on host {}, skipping discovery".format( host ) )
+ discoveryResult = main.FALSE
+ if cmd:
+ self.runCmdOnHost( host, flushCmd )
+ self.runCmdOnHost( host, cmd )
+ return discoveryResult
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
diff --git a/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py b/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
index ac909ee..08f691a 100644
--- a/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
+++ b/TestON/drivers/common/cli/ofdpa/ofdpaswitchdriver.py
@@ -38,6 +38,7 @@
"""
super( CLI, self ).__init__()
self.name = None
+ self.shortName = None
self.handle = None
self.prompt = "~#"
# Respect to bin folder
@@ -46,6 +47,8 @@
self.tempDirectory = "/tmp/"
self.conf = "ofagent.conf"
self.switchDirectory = "/etc/ofagent/"
+ self.ports = []
+ self.isup = False
def connect( self, **connectargs ):
"""
@@ -57,9 +60,19 @@
vars( self )[ key ] = connectargs[ key ]
# Get the name
self.name = self.options[ 'name' ]
+ self.shortName = self.options[ 'shortName' ]
# Get the dpid
self.dpid = self.options[ 'dpid' ]
# Get ofagent patch
+ for key, value in self.options.items():
+ if re.match( 'link[\d]+', key ):
+ self.ports.append( { 'enabled': True,
+ 'ips': [ None ],
+ 'mac': None,
+ 'name': None,
+ 'node2': value[ 'node2' ],
+ 'port2': value[ 'port2' ],
+ 'of_port': value[ 'port1' ] } )
if 'confDir' in self.options:
self.switchDirectory = self.options[ 'confDir' ]
# Parse the IP address
@@ -165,7 +178,7 @@
onosIp = "-t " + str( ip )
elif isinstance( ip, types.ListType ):
for ipAddress in ip:
- onosIp += "-t " + str( ipAddress ) + " "
+ onosIp += "-t " + str( ipAddress ) + " "
else:
main.log.error( self.name + ": Invalid ip address" )
return main.FALSE
@@ -189,6 +202,10 @@
kwargs={},
attempts=10,
sleep=10)
+ if not assignResult:
+ self.isup = False
+ else:
+ self.isup = True
# Done return true
return assignResult
# Errors handling
@@ -221,10 +238,21 @@
"""
Create a backup file of the old configuration on the switch
"""
- self.handle.sendline( "" )
- self.handle.expect( self.prompt )
- self.handle.sendline( "cp %s%s %s%s.backup" % (self.switchDirectory, self.conf, self.switchDirectory, self.conf) )
- self.handle.expect( self.prompt )
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "cp %s%s %s%s.backup" % (self.switchDirectory, self.conf, self.switchDirectory, self.conf) )
+ self.handle.expect( self.prompt )
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
def pushCfg( self ):
"""
@@ -236,23 +264,75 @@
os.system( "scp " + self.tempDirectory + self.conf + " " +
self.user_name + "@" + self.ip_address + ":" + self.switchDirectory)
+ def ofagentIsRunning( self ):
+ """
+ Return main.TRUE if service ofagentd is running on the
+ switch; otherwise main.FALSE
+ """
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "service ofagentd status" )
+ self.handle.expect( self.prompt )
+ response = self.handle.before
+ if "ofagentd is running" in response:
+ return main.TRUE
+ else:
+ return main.FALSE
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
+
def startOfAgent( self ):
"""
Start the ofagent on the device
"""
- self.handle.sendline( "" )
- self.handle.expect( self.prompt )
- self.handle.sendline( "service ofagentd start" )
- self.handle.expect( self.prompt )
+ try:
+ if self.ofagentIsRunning():
+ main.log.warn( self.name + ": ofagentd is already running" )
+ return main.TRUE
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "service ofagentd start" )
+ self.handle.expect( self.prompt )
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
def stopOfAgent( self ):
"""
Stop the ofagent on the device
"""
- self.handle.sendline( "" )
- self.handle.expect( self.prompt )
- self.handle.sendline( "service ofagentd stop" )
- self.handle.expect( self.prompt )
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "service ofagentd stop" )
+ self.handle.expect( self.prompt )
+ self.isup = False
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
def dumpFlows( self ):
"""
@@ -318,19 +398,89 @@
Enable all the ports on the devices
It needs to wait for the boot
"""
- self.handle.sendline( "" )
- self.handle.expect( self.prompt )
- self.handle.sendline( "client_port_table_dump" )
- self.handle.expect( self.prompt )
- response = self.handle.before
- if "Error from ofdpaClientInitialize()" in response:
- main.log.warn( self.name + ": Not yet started" )
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "client_port_table_dump" )
+ self.handle.expect( self.prompt )
+ response = self.handle.before
+ if "Error from ofdpaClientInitialize()" in response:
+ main.log.warn( self.name + ": Not yet started" )
+ return main.FALSE
+ # Change port speed
+ self.handle.sendline( "sh portspeed" )
+ self.handle.expect( self.prompt )
+ response = self.handle.before
+ if "Failure calling" in response:
+ main.log.warn( self.name + ": failed to change port speed" )
+ return main.FALSE
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
return main.FALSE
- # Change port speed
- self.handle.sendline( "sh portspeed" )
- self.handle.expect( self.prompt )
- response = self.handle.before
- if "Failure calling" in response:
- main.log.warn( self.name + ": failed to change port speed" )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
+
+ def setPortSpeed( self, index, speed=40000 ):
+ """
+ Run client_drivshell on the switch to set speed for a
+ specific port
+ index: port index, e.g. 1
+ speed: port speed, e.g. 40000
+ """
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ cmd = "client_drivshell port {} sp={}".format( index, speed )
+ self.handle.sendline( cmd )
+ self.handle.expect( self.prompt )
+ response = self.handle.before
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
return main.FALSE
- return main.TRUE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
+
+ def updatePorts( self ):
+ """
+ Get latest port status on the switch by running
+ client_port_table_dump commmand and parsing the output
+ """
+ try:
+ self.handle.sendline( "" )
+ self.handle.expect( self.prompt )
+ self.handle.sendline( "client_port_table_dump" )
+ self.handle.expect( self.prompt )
+ ports = self.handle.before
+ if "Error from ofdpaClientInitialize()" in ports:
+ main.log.warn( self.name + ": Not yet started" )
+ return main.FALSE
+ ports = re.findall( r"0x[\d]+.*port[\d]+:\r\r\n.*\r\r\n.*PeerFeature:.*\r\r\n", ports )
+ for port in ports:
+ m = re.match( r".*port([\d]+):\r\r\n.*state = (.*), mac", port )
+ index = m.group( 1 )
+ enabled = True if m.group( 2 ) == '0x00000000' else False
+ for p in self.ports:
+ if p[ 'of_port' ] == index:
+ p[ 'enabled' ] = enabled
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": pexpect.TIMEOUT found" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()