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:
+114
-33
@@ -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 )
|
||||||
|
|||||||
@@ -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 )
|
||||||
|
|||||||
Executable
+22
@@ -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)
|
||||||
Reference in New Issue
Block a user