Merge pull request #641 from xiaozhou/master

add a new type of RemoteLink in cluster edition that uses GRE tunnels
This commit is contained in:
lantz
2016-08-16 15:05:59 -07:00
committed by GitHub
3 changed files with 138 additions and 34 deletions
+114 -33
View File
@@ -287,8 +287,8 @@ class RemoteMixin( object ):
return super( RemoteMixin, self).popen( *args, tt=False, **kwargs ) return super( RemoteMixin, self).popen( *args, tt=False, **kwargs )
def addIntf( self, *args, **kwargs ): def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf" # "Override: use RemoteLink.moveIntf"
kwargs.update( moveIntfFn=RemoteLink.moveIntf ) # kwargs.update( moveIntfFn=RemoteLink.moveIntf )
return super( RemoteMixin, self).addIntf( *args, **kwargs ) return super( RemoteMixin, self).addIntf( *args, **kwargs )
@@ -401,25 +401,23 @@ class RemoteLink( Link ):
printError: if true, print error""" printError: if true, print error"""
intf = str( intf ) intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, node.pid ) cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd ) result = node.rcmd( cmd )
links = node.cmd( 'ip link show' ) if result:
if not ' %s:' % intf in links: raise Exception('error executing command %s' % cmd)
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
return True return True
def makeTunnel( self, node1, node2, intfname1, intfname2, def makeTunnel( self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None ): addr1=None, addr2=None ):
"Make a tunnel across switches on different servers" "Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves! # We should never try to create a tunnel to ourselves!
assert node1.server != 'localhost' or node2.server != 'localhost' assert node1.server != node2.server
# And we can't ssh into this server remotely as 'localhost', # And we can't ssh into this server remotely as 'localhost',
# so try again swappping node1 and node2 # so try again swappping node1 and node2
if node2.server == 'localhost': if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1, return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 ) addr2, addr1 )
print( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
# 1. Create tap interfaces # 1. Create tap interfaces
for node in node1, node2: for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename # For now we are hard-wiring tap9, which we will rename
@@ -475,6 +473,87 @@ class RemoteLink( Link ):
return result return result
class RemoteSSHLink( RemoteLink ):
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
GRE_KEY = 0
class RemoteGRELink( RemoteLink ):
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == server2:
# Link within same server
Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
# Need to reduce the MTU of all emulated hosts to 1450 for GRE
# tunneling, otherwise packets larger than 1400 bytes cannot be
# successfully transmitted through the tunnel.
node1.cmd('ip link set dev %s mtu 1450' % intfname1)
node2.cmd('ip link set dev %s mtu 1450' % intfname2)
else:
# Otherwise, make a tunnel
self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
self.tunnel = 1
def makeTunnel(self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != node2.server
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
IP1, IP2 = node1.serverIP, node2.serverIP
# GRE tunnel needs to be set up with the IP of the local interface
# that connects the remote node, NOT '127.0.0.1' of localhost
if node1.server == 'localhost':
output = quietRun('ip route get %s' % node2.serverIP)
IP1 = output.split(' src ')[1].split()[0]
print( '\n*** Make GRE tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
tun1 = 'local ' + IP1 + ' remote ' + IP2
tun2 = 'local ' + IP2 + ' remote ' + IP1
global GRE_KEY
GRE_KEY += 1
for (node, intfname, addr, tun) in [(node1, intfname1, addr1, tun1),
(node2, intfname2, addr2, tun2)]:
node.rcmd('ip link delete ' + intfname)
result = node.rcmd('ip link add name ' + intfname + ' type gretap '
+ tun + ' ttl 64 key ' + str(GRE_KEY))
if result:
raise Exception('error creating gretap on %s: %s'
% (node, result))
if addr:
node.rcmd('ip link set %s address %s' % (intfname, addr))
node.rcmd('ip link set dev %s up' % intfname)
node.rcmd('ip link set dev %s mtu 1450' % intfname)
if not self.moveIntf(intfname, node):
raise Exception('interface move failed on node %s' % node)
# Some simple placement algorithms for MininetCluster # Some simple placement algorithms for MininetCluster
class Placer( object ): class Placer( object ):
@@ -776,11 +855,11 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs ) Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels(): def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
"Test tunnels between nodes in namespaces" "Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=RemoteLink ) net = Mininet( host=RemoteHost, link=link )
h1 = net.addHost( 'h1' ) h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' ) h2 = net.addHost( 'h2', server=remote )
net.addLink( h1, h2 ) net.addLink( h1, h2 )
net.start() net.start()
net.pingAll() net.pingAll()
@@ -791,11 +870,10 @@ def testNsTunnels():
# This shows how node options may be used to manage # This shows how node options may be used to manage
# cluster placement using the net.add*() API # cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2' ): def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
"Test remote Node classes" "Test remote Node classes"
print( '*** Remote Node Test' ) print( '*** Remote Node Test' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link )
link=RemoteLink )
c0 = net.addController( 'c0' ) c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address # Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP() Intf( 'eth0', node=c0 ).updateIP()
@@ -852,11 +930,11 @@ def ClusterController( *args, **kwargs):
Intf( 'eth0', node=controller ).updateIP() Intf( 'eth0', node=controller ).updateIP()
return controller return controller
def testRemoteTopo(): def testRemoteTopo( link=RemoteGRELink ):
"Test remote Node classes using Mininet()/Topo() API" "Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 ) topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer, net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=RemoteLink, controller=ClusterController ) link=link, controller=ClusterController )
net.start() net.start()
net.pingAll() net.pingAll()
net.stop() net.stop()
@@ -866,18 +944,17 @@ def testRemoteTopo():
# do random switch placement rather than completely random # do random switch placement rather than completely random
# host placement. # host placement.
def testRemoteSwitches(): def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
"Test with local hosts and remote switches" "Test with local hosts and remote switches"
servers = [ 'localhost', 'ubuntu2'] servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 ) topo = TreeTopo( depth=4, fanout=2 )
net = MininetCluster( topo=topo, servers=servers, net = MininetCluster( topo=topo, servers=servers, link=link,
placement=RoundRobinPlacer ) placement=RoundRobinPlacer )
net.start() net.start()
net.pingAll() net.pingAll()
net.stop() net.stop()
#
# For testing and demo purposes it would be nice to draw the # For testing and demo purposes it would be nice to draw the
# network graph and color it based on server. # network graph and color it based on server.
@@ -885,19 +962,19 @@ def testRemoteSwitches():
# functions, for maximum ease of use. MininetCluster() also # functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections. # pre-flights and multiplexes server connections.
def testMininetCluster(): def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
"Test MininetCluster()" "Test MininetCluster()"
servers = [ 'localhost', 'ubuntu2' ] servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 ) topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers, net = MininetCluster( topo=topo, servers=servers, link=link,
placement=SwitchBinPlacer ) placement=SwitchBinPlacer )
net.start() net.start()
net.pingAll() net.pingAll()
net.stop() net.stop()
def signalTest(): def signalTest( remote='ubuntu2'):
"Make sure hosts are robust to signals" "Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server='ubuntu1' ) h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT ) h.shell.send_signal( SIGINT )
h.shell.poll() h.shell.poll()
if h.shell.returncode is None: if h.shell.returncode is None:
@@ -906,10 +983,14 @@ def signalTest():
print( 'FAILURE:', h, 'exited with code', h.shell.returncode ) print( 'FAILURE:', h, 'exited with code', h.shell.returncode )
h.stop() h.stop()
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'info' ) setLogLevel( 'info' )
# testRemoteTopo() remoteServer = 'ubuntu2'
# testRemoteNet() remoteLink = RemoteSSHLink
# testMininetCluster() testRemoteTopo(link=remoteLink)
# testRemoteSwitches() testNsTunnels( remote=remoteServer, link=remoteLink )
signalTest() testRemoteNet( remote=remoteServer, link=remoteLink)
testMininetCluster( remote=remoteServer, link=remoteLink)
testRemoteSwitches( remote=remoteServer, link=remoteLink)
signalTest( remote=remoteServer )
+2 -1
View File
@@ -3,6 +3,7 @@
"clusterdemo.py: demo of Mininet Cluster Edition prototype" "clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
from cluster import RemoteLink, RemoteSSHLink, RemoteGRELink
from mininet.topolib import TreeTopo from mininet.topolib import TreeTopo
from mininet.log import setLogLevel from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI from mininet.examples.clustercli import ClusterCLI as CLI
@@ -11,7 +12,7 @@ def demo():
"Simple Demo of Cluster Mode" "Simple Demo of Cluster Mode"
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ] servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
topo = TreeTopo( depth=3, fanout=3 ) topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers, net = MininetCluster( topo=topo, servers=servers, Link=RemoteLink,
placement=SwitchBinPlacer ) placement=SwitchBinPlacer )
net.start() net.start()
CLI( net ) CLI( net )
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
from mininet.net import Mininet
from mininet.log import setLogLevel
def perf(Link):
net = Mininet( host=RemoteHost, link=Link )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf(RemoteSSHLink)
perf(RemoteGRELink)