From da594dcecde950f1f767006ce01eb46e94688f4f Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Mon, 11 Jul 2016 21:05:01 -0700 Subject: [PATCH 1/3] add a new type of RemoteLink in cluster edition that uses GRE tunnels --- examples/cluster.py | 147 +++++++++++++++++++++++++++++++--------- examples/clusterdemo.py | 3 +- examples/clusterperf.py | 22 ++++++ 3 files changed, 138 insertions(+), 34 deletions(-) create mode 100755 examples/clusterperf.py diff --git a/examples/cluster.py b/examples/cluster.py index 3639f3e..642e0d2 100755 --- a/examples/cluster.py +++ b/examples/cluster.py @@ -288,8 +288,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 ) @@ -402,25 +402,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 @@ -476,6 +474,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 turnnel. + 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 det %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 ): @@ -777,11 +856,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() @@ -792,11 +871,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() @@ -853,11 +931,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() @@ -867,18 +945,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. @@ -886,19 +963,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: @@ -907,10 +984,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 ) diff --git a/examples/clusterdemo.py b/examples/clusterdemo.py index 6e050c5..66d510e 100755 --- a/examples/clusterdemo.py +++ b/examples/clusterdemo.py @@ -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 ) diff --git a/examples/clusterperf.py b/examples/clusterperf.py new file mode 100755 index 0000000..7531aec --- /dev/null +++ b/examples/clusterperf.py @@ -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) From 7630861fde99ad0866e42400b49df6bb5dcde31f Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Mon, 11 Jul 2016 21:15:04 -0700 Subject: [PATCH 2/3] fix typo --- examples/cluster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cluster.py b/examples/cluster.py index 642e0d2..4a34b7b 100755 --- a/examples/cluster.py +++ b/examples/cluster.py @@ -510,7 +510,7 @@ class RemoteGRELink( RemoteLink ): 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 turnnel. + # 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: From d8371615cece2570c25c0771c0d38f7761d11224 Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Fri, 29 Jul 2016 09:57:30 -0700 Subject: [PATCH 3/3] fix typo --- examples/cluster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cluster.py b/examples/cluster.py index 4a34b7b..64e8a5d 100755 --- a/examples/cluster.py +++ b/examples/cluster.py @@ -547,7 +547,7 @@ class RemoteGRELink( RemoteLink ): raise Exception('error creating gretap on %s: %s' % (node, result)) if addr: - node.rcmd('ip link det %s address %s' % (intfname, 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)