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 )
|
||||
|
||||
def addIntf( self, *args, **kwargs ):
|
||||
"Override: use RemoteLink.moveIntf"
|
||||
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
|
||||
# "Override: use RemoteLink.moveIntf"
|
||||
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
|
||||
return super( RemoteMixin, self).addIntf( *args, **kwargs )
|
||||
|
||||
|
||||
@@ -401,25 +401,23 @@ class RemoteLink( Link ):
|
||||
printError: if true, print error"""
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
|
||||
node.rcmd( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ' %s:' % intf in links:
|
||||
if printError:
|
||||
error( '*** Error: RemoteLink.moveIntf: ' + intf +
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
result = node.rcmd( cmd )
|
||||
if result:
|
||||
raise Exception('error executing command %s' % cmd)
|
||||
return True
|
||||
|
||||
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 != 'localhost' or node2.server != 'localhost'
|
||||
assert node1.server != node2.server
|
||||
# And we can't ssh into this server remotely as 'localhost',
|
||||
# so try again swappping node1 and node2
|
||||
if node2.server == 'localhost':
|
||||
return self.makeTunnel( node2, node1, intfname2, intfname1,
|
||||
addr2, addr1 )
|
||||
print( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
|
||||
' == ' + node2.server + ':' + intfname2 )
|
||||
# 1. Create tap interfaces
|
||||
for node in node1, node2:
|
||||
# For now we are hard-wiring tap9, which we will rename
|
||||
@@ -475,6 +473,87 @@ class RemoteLink( Link ):
|
||||
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
|
||||
|
||||
class Placer( object ):
|
||||
@@ -776,11 +855,11 @@ class MininetCluster( Mininet ):
|
||||
Mininet.buildFromTopo( self, *args, **kwargs )
|
||||
|
||||
|
||||
def testNsTunnels():
|
||||
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
|
||||
"Test tunnels between nodes in namespaces"
|
||||
net = Mininet( host=RemoteHost, link=RemoteLink )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2', server='ubuntu2' )
|
||||
net = Mininet( host=RemoteHost, link=link )
|
||||
h1 = net.addHost( 'h1')
|
||||
h2 = net.addHost( 'h2', server=remote )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
@@ -791,11 +870,10 @@ def testNsTunnels():
|
||||
# This shows how node options may be used to manage
|
||||
# cluster placement using the net.add*() API
|
||||
|
||||
def testRemoteNet( remote='ubuntu2' ):
|
||||
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
|
||||
"Test remote Node classes"
|
||||
print( '*** Remote Node Test' )
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
|
||||
link=RemoteLink )
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link )
|
||||
c0 = net.addController( 'c0' )
|
||||
# Make sure controller knows its non-loopback address
|
||||
Intf( 'eth0', node=c0 ).updateIP()
|
||||
@@ -852,11 +930,11 @@ def ClusterController( *args, **kwargs):
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def testRemoteTopo():
|
||||
def testRemoteTopo( link=RemoteGRELink ):
|
||||
"Test remote Node classes using Mininet()/Topo() API"
|
||||
topo = LinearTopo( 2 )
|
||||
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
|
||||
link=RemoteLink, controller=ClusterController )
|
||||
link=link, controller=ClusterController )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
@@ -866,18 +944,17 @@ def testRemoteTopo():
|
||||
# do random switch placement rather than completely random
|
||||
# host placement.
|
||||
|
||||
def testRemoteSwitches():
|
||||
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
|
||||
"Test with local hosts and remote switches"
|
||||
servers = [ 'localhost', 'ubuntu2']
|
||||
servers = [ 'localhost', remote]
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
net = MininetCluster( topo=topo, servers=servers, link=link,
|
||||
placement=RoundRobinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
|
||||
#
|
||||
# For testing and demo purposes it would be nice to draw the
|
||||
# network graph and color it based on server.
|
||||
|
||||
@@ -885,19 +962,19 @@ def testRemoteSwitches():
|
||||
# functions, for maximum ease of use. MininetCluster() also
|
||||
# pre-flights and multiplexes server connections.
|
||||
|
||||
def testMininetCluster():
|
||||
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
|
||||
"Test MininetCluster()"
|
||||
servers = [ 'localhost', 'ubuntu2' ]
|
||||
servers = [ 'localhost', remote ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
net = MininetCluster( topo=topo, servers=servers, link=link,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
def signalTest():
|
||||
def signalTest( remote='ubuntu2'):
|
||||
"Make sure hosts are robust to signals"
|
||||
h = RemoteHost( 'h0', server='ubuntu1' )
|
||||
h = RemoteHost( 'h0', server=remote )
|
||||
h.shell.send_signal( SIGINT )
|
||||
h.shell.poll()
|
||||
if h.shell.returncode is None:
|
||||
@@ -906,10 +983,14 @@ def signalTest():
|
||||
print( 'FAILURE:', h, 'exited with code', h.shell.returncode )
|
||||
h.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# testRemoteTopo()
|
||||
# testRemoteNet()
|
||||
# testMininetCluster()
|
||||
# testRemoteSwitches()
|
||||
signalTest()
|
||||
remoteServer = 'ubuntu2'
|
||||
remoteLink = RemoteSSHLink
|
||||
testRemoteTopo(link=remoteLink)
|
||||
testNsTunnels( remote=remoteServer, link=remoteLink )
|
||||
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"
|
||||
|
||||
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
|
||||
from cluster import RemoteLink, RemoteSSHLink, RemoteGRELink
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
@@ -11,7 +12,7 @@ def demo():
|
||||
"Simple Demo of Cluster Mode"
|
||||
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
net = MininetCluster( topo=topo, servers=servers, Link=RemoteLink,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
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