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 )
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 )