diff --git a/mininet/link.py b/mininet/link.py index 747f2bd364c39213bd73cb962ce8a2f0558ba7c9..74b9821661587bb64a9c78d8cf220ac8952b1a36 100644 --- a/mininet/link.py +++ b/mininet/link.py @@ -32,7 +32,7 @@ class Intf( object ): "Basic interface object that can configure itself." - def __init__( self, name, node=None, port=None, link=None, **params ): + def __init__( self, name, node=None, port=None, link=None, mac=None, **params ): """name: interface name (e.g. h1-eth0) node: owning node (where this intf most likely lives) link: parent link if we're part of a link @@ -40,7 +40,13 @@ def __init__( self, name, node=None, port=None, link=None, **params ): self.node = node self.name = name self.link = link - self.mac, self.ip, self.prefixLen = None, None, None + self.mac = mac + self.ip, self.prefixLen = None, None + + # if interface is lo, we know the ip is 127.0.0.1. + # This saves an ifconfig command per node + if self.name == 'lo': + self.ip = '127.0.0.1' # Add to node (and move ourselves if necessary ) node.addIntf( self, port=port ) # Save params for future reference @@ -91,6 +97,19 @@ def updateMAC( self ): self.mac = macs[ 0 ] if macs else None return self.mac + # Instead of updating ip and mac separately, + # use one ifconfig call to do it simultaneously. + # This saves an ifconfig command, which improves performance. + + def updateAddr( self ): + "Return IP address and MAC address based on ifconfig." + ifconfig = self.ifconfig() + ips = self._ipMatchRegex.findall( ifconfig ) + macs = self._macMatchRegex.findall( ifconfig ) + self.ip = ips[ 0 ] if ips else None + self.mac = macs[ 0 ] if macs else None + return self.ip, self.mac + def IP( self ): "Return IP address" return self.ip @@ -102,8 +121,15 @@ def MAC( self ): def isUp( self, setUp=False ): "Return whether interface is up" if setUp: - self.ifconfig( 'up' ) - return "UP" in self.ifconfig() + cmdOutput = self.ifconfig( 'up' ) + # no output indicates success + if cmdOutput: + error( "Error setting %s up: %s " % ( self.name, cmdOutput ) ) + return False + else: + return True + else: + return "UP" in self.ifconfig() def rename( self, newname ): "Rename interface" @@ -154,8 +180,6 @@ def config( self, mac=None, ip=None, ifconfig=None, self.setParam( r, 'setIP', ip=ip ) self.setParam( r, 'isUp', up=up ) self.setParam( r, 'ifconfig', ifconfig=ifconfig ) - self.updateIP() - self.updateMAC() return r def delete( self ): @@ -328,7 +352,7 @@ class Link( object ): Other types of links could be tunnels, link emulators, etc..""" def __init__( self, node1, node2, port1=None, port2=None, - intfName1=None, intfName2=None, + intfName1=None, intfName2=None, addr1=None, addr2=None, intf=Intf, cls1=None, cls2=None, params1=None, params2=None ): """Create veth link to another node, making two new interfaces. @@ -354,7 +378,7 @@ def __init__( self, node1, node2, port1=None, port2=None, if not intfName2: intfName2 = self.intfName( node2, port2 ) - self.makeIntfPair( intfName1, intfName2 ) + self.makeIntfPair( intfName1, intfName2, addr1, addr2 ) if not cls1: cls1 = intf @@ -366,9 +390,9 @@ def __init__( self, node1, node2, port1=None, port2=None, params2 = {} intf1 = cls1( name=intfName1, node=node1, port=port1, - link=self, **params1 ) + link=self, mac=addr1, **params1 ) intf2 = cls2( name=intfName2, node=node2, port=port2, - link=self, **params2 ) + link=self, mac=addr2, **params2 ) # All we are is dust in the wind, and our two interfaces self.intf1, self.intf2 = intf1, intf2 @@ -379,13 +403,13 @@ def intfName( cls, node, n ): return node.name + '-eth' + repr( n ) @classmethod - def makeIntfPair( cls, intf1, intf2 ): + def makeIntfPair( cls, intf1, intf2, addr1=None, addr2=None ): """Create pair of interfaces intf1: name of interface 1 intf2: name of interface 2 (override this class method [and possibly delete()] to change link type)""" - makeIntfPair( intf1, intf2 ) + makeIntfPair( intf1, intf2, addr1, addr2 ) def delete( self ): "Delete this link" @@ -398,10 +422,12 @@ def __str__( self ): class TCLink( Link ): "Link with symmetric TC interfaces configured via opts" def __init__( self, node1, node2, port1=None, port2=None, - intfName1=None, intfName2=None, **params ): + intfName1=None, intfName2=None, + addr1=None, addr2=None, **params ): Link.__init__( self, node1, node2, port1=port1, port2=port2, intfName1=intfName1, intfName2=intfName2, cls1=TCIntf, cls2=TCIntf, + addr1=addr1, addr2=addr2, params1=params, params2=params) diff --git a/mininet/net.py b/mininet/net.py index 880780401847226905b183a4ddf1a791c3b70f0e..a4477d37aa03c0b76823b303698949d4bef82bc9 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -90,6 +90,7 @@ import re import select import signal +import random import copy from time import sleep from itertools import chain, groupby @@ -326,8 +327,12 @@ def addLink( self, node1, node2, port1=None, port2=None, port1: source port port2: dest port returns: link object""" + mac1 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 ) + mac2 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 ) defaults = { 'port1': port1, 'port2': port2, + 'addr1': mac1, + 'addr2': mac2, 'intf': self.intf } defaults.update( params ) if not cls: @@ -350,7 +355,6 @@ def configHosts( self ): # quietRun( 'renice +18 -p ' + repr( host.pid ) ) # This may not be the right place to do this, but # it needs to be done somewhere. - host.cmd( 'ifconfig lo up' ) info( '\n' ) def buildFromTopo( self, topo=None ): diff --git a/mininet/node.py b/mininet/node.py index 7eb632217774fa76b0979d560569e1826b2c9814..19f26d7f5ce36b453d347c89198eb402dcb8a62c 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -924,7 +924,6 @@ def start( self, controllers ): for c in controllers ] ) ofdlog = '/tmp/' + self.name + '-ofd.log' ofplog = '/tmp/' + self.name + '-ofp.log' - self.cmd( 'ifconfig lo up' ) intfs = [ str( i ) for i in self.intfList() if not i.IP() ] self.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' + self.name + ' -d %s ' % self.dpid + @@ -975,7 +974,6 @@ def setup( cls ): def start( self, controllers ): "Start up kernel datapath." ofplog = '/tmp/' + self.name + '-ofp.log' - quietRun( 'ifconfig lo up' ) # Delete local datapath if it exists; # then create a new one monitoring the given interfaces self.cmd( 'ovs-dpctl del-dp ' + self.dp ) @@ -1093,9 +1091,6 @@ def start( self, controllers ): if self.inNamespace: raise Exception( 'OVS kernel switch does not work in a namespace' ) - # We should probably call config instead, but this - # requires some rethinking... - self.cmd( 'ifconfig lo up' ) # Annoyingly, --if-exists option seems not to work self.cmd( 'ovs-vsctl del-br', self ) int( self.dpid, 16 ) # DPID must be a hex string @@ -1200,7 +1195,6 @@ def start( self, controllers ): logfile = '/tmp/ivs.%s.log' % self.name - self.cmd( 'ifconfig lo up' ) self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' ) def stop( self ): @@ -1240,7 +1234,6 @@ def __init__( self, name, inNamespace=False, command='controller', self.protocol = protocol Node.__init__( self, name, inNamespace=inNamespace, ip=ip, **params ) - self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary self.checkListening() def checkListening( self ): diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index c888e29620a4edfd4aea6468feff43be11c44ba8..d5a09f93476c6f51f9346007a59102df34189078 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -45,7 +45,7 @@ def runOptionsTopoTest( self, n, hopts=None, lopts=None ): mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts, lopts=lopts ), host=CPULimitedHost, link=TCLink, - switch=self.switchClass ) + switch=self.switchClass, waitConnected=True ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) @@ -67,7 +67,8 @@ def testCPULimits( self ): #self.runOptionsTopoTest( N, hopts=hopts ) mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ), - host=CPULimitedHost, switch=self.switchClass ) + host=CPULimitedHost, switch=self.switchClass, + waitConnected=True ) mn.start() results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) mn.stop() @@ -77,13 +78,16 @@ def testCPULimits( self ): def testLinkBandwidth( self ): "Verify that link bandwidths are accurate within a bound." - BW = .5 # Mbps + if self.switchClass is UserSwitch: + self.skipTest ( 'UserSwitch has very poor performance, so skip for now' ) + BW = 5 # Mbps BW_TOLERANCE = 0.8 # BW fraction below which test should fail # Verify ability to create limited-link topo first; lopts = { 'bw': BW, 'use_htb': True } # Also verify correctness of limit limitng within a bound. mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), - link=TCLink, switch=self.switchClass ) + link=TCLink, switch=self.switchClass, + waitConnected=True ) bw_strs = mn.run( mn.iperf, format='m' ) for bw_str in bw_strs: bw = float( bw_str.split(' ')[0] ) @@ -95,7 +99,8 @@ def testLinkDelay( self ): DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), - link=TCLink, switch=self.switchClass, autoStaticArp=True ) + link=TCLink, switch=self.switchClass, autoStaticArp=True, + waitConnected=True ) ping_delays = mn.run( mn.pingFull ) test_outputs = ping_delays[0] # Ignore unused variables below @@ -117,7 +122,8 @@ def testLinkLoss( self ): lopts = { 'loss': LOSS_PERCENT, 'use_htb': True } mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ), host=CPULimitedHost, link=TCLink, - switch=self.switchClass ) + switch=self.switchClass, + waitConnected=True ) # Drops are probabilistic, but the chance of no dropped packets is # 1 in 100 million with 4 hops for a link w/99% loss. dropped_total = 0 diff --git a/mininet/util.py b/mininet/util.py index 5cb27f449d5ecad2dc62691c218307ab97ae18d0..69d31273f21ed858d35aca3004c74f3e17d6597e 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -145,7 +145,7 @@ def isShellBuiltin( cmd ): # live in the root namespace and thus do not have to be # explicitly moved. -def makeIntfPair( intf1, intf2 ): +def makeIntfPair( intf1, intf2, addr1=None, addr2=None ): """Make a veth pair connecting intf1 and intf2. intf1: string, interface intf2: string, interface @@ -154,7 +154,11 @@ def makeIntfPair( intf1, intf2 ): quietRun( 'ip link del ' + intf1 ) quietRun( 'ip link del ' + intf2 ) # Create new pair - cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 + if addr1 is None and addr2 is None: + cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 + else: + cmd = ( 'ip link add name ' + intf1 + ' address ' + addr1 + + ' type veth peer name ' + intf2 + ' address ' + addr2 ) cmdOutput = quietRun( cmd ) if cmdOutput == '': return True @@ -185,10 +189,12 @@ def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ): intf = str( intf ) cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid ) if srcNode: - srcNode.cmd( cmd ) + cmdOutput = srcNode.cmd( cmd ) else: - quietRun( cmd ) - if ( ' %s:' % intf ) not in dstNode.cmd( 'ip link show', intf ): + cmdOutput = quietRun( cmd ) + # If ip link set does not produce any output, then we can assume + # that the link has been moved successfully. + if cmdOutput: if printError: error( '*** Error: moveIntf: ' + intf + ' not successfully moved to ' + dstNode.name + '\n' )