Fixes for cluster.py (#1035)

- use decode() for python 3 compatibility
- try to identify non-loopback interface for controllers
-- avoid hardwired 'eth0'
- use remoteServer consistently in tests
- pass tests on ubuntu 20/python 3
- pass hybrid test with hybrid python 2 and python 3 mininet
This commit is contained in:
lantz
2021-02-08 22:28:12 -08:00
committed by GitHub
parent 5d4ec1ab9e
commit 39ed456de2
+57 -31
View File
@@ -89,7 +89,7 @@ from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, errRun
from mininet.util import quietRun, errRun, decode
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
@@ -247,7 +247,7 @@ class RemoteMixin( object ):
result = ''
while True:
poll = popen.poll()
result += popen.stdout.read()
result += decode( popen.stdout.read() )
if poll is not None:
break
return result
@@ -440,13 +440,16 @@ class RemoteLink( Link ):
# When we receive the character '@', it means that our
# tunnel should be set up
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
ch = decode( tunnel.stdout.read( 1 ) )
if ch != '@':
raise Exception( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
ch += decode( tunnel.stdout.read() )
cmd = ' '.join( cmd )
raise Exception( 'makeTunnel:\n'
'Tunnel setup failed for '
'%s:%s' % ( node1, node1.dest ) + ' to '
'%s:%s\n' % ( node2, node2.dest ) +
'command was: %s' % cmd + '\n' +
'result was: ' + ch )
# 3. Move interfaces if necessary
for node in node1, node2:
if not self.moveIntf( 'tap9', node ):
@@ -848,27 +851,37 @@ class MininetCluster( Mininet ):
if cfile:
config.setdefault( 'controlPath', cfile )
@staticmethod
def isLoopback( ipaddr ):
"Is ipaddr an IPv4 loopback address?"
return ipaddr.startswith( '127.' )
# pylint: disable=arguments-differ,signature-differs
def addController( self, *args, **kwargs ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
loopback = '127.0.0.1'
controllerIP = controller.IP()
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return None
not self.isLoopback( controller.IP() ) ):
return controller
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip is not controller.IP() ]
if ip != controllerIP ]
if not serverIPs:
return None # no remote servers - loopback is fine
remoteIP = serverIPs[ 0 ]
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
for remoteIP in serverIPs:
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'addController: could not find external '
'interface/IP for %s' % controller )
debug( 'adding', intf, 'to', controller )
Intf( intf, node=controller ).updateIP()
debug( controller, 'IP address updated to', controller.IP() )
@@ -883,7 +896,10 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# Default remote server for tests
remoteServer = 'ubuntu2'
def testNsTunnels( remote=remoteServer, link=RemoteGRELink ):
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
h1 = net.addHost( 'h1')
@@ -898,14 +914,14 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteNet( remote=remoteServer, link=RemoteGRELink ):
"Test remote Node classes"
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link,
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=link, controller=ClusterController,
waitConnected=True )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
info( "*** Creating local h1\n" )
h1 = net.addHost( 'h1' )
info( "*** Creating remote h2\n" )
@@ -937,7 +953,7 @@ def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
remoteHosts = [ 'h2' ]
remoteSwitches = [ 's2' ]
remoteServer = 'ubuntu2'
def HostPlacer( name, *args, **params ):
"Custom Host() constructor which places hosts on servers"
@@ -954,10 +970,21 @@ def SwitchPlacer( name, *args, **params ):
return RemoteOVSSwitch( name, *args, **params )
def ClusterController( *args, **kwargs):
"Custom Controller() constructor which updates its eth0 IP address"
"Custom Controller() constructor which updates its intf IP address"
intf = kwargs.pop( 'intf', '' )
controller = Controller( *args, **kwargs )
# Find out its IP address so that cluster switches can connect
Intf( 'eth0', node=controller ).updateIP()
if not intf:
output = controller.cmd(
r"ip a | egrep -o '\w+:\s\w+'" ).split( '\n' )
for line in output:
intf = line.split()[ -1 ]
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'Could not find non-loopback interface'
'for %s' % controller )
Intf( intf, node=controller ).updateIP()
return controller
def testRemoteTopo( link=RemoteGRELink ):
@@ -974,7 +1001,7 @@ def testRemoteTopo( link=RemoteGRELink ):
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteSwitches( remote=remoteServer, link=RemoteGRELink ):
"Test with local hosts and remote switches"
servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 )
@@ -992,7 +1019,7 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
def testMininetCluster( remote=remoteServer, link=RemoteGRELink ):
"Test MininetCluster()"
servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 )
@@ -1002,7 +1029,7 @@ def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
net.pingAll()
net.stop()
def signalTest( remote='ubuntu2'):
def signalTest( remote=remoteServer):
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT )
@@ -1017,7 +1044,6 @@ def signalTest( remote='ubuntu2'):
if __name__ == '__main__':
setLogLevel( 'info' )
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )