Moved link creation into Node.addIntf() and fixed util:CreateLink().

Also tweaked cmd/cmdPrint to make them more beautiful and functional,
and a few other minor changes, such as using a new idiom for verbose.

These changes support reinstating the examples.
This commit is contained in:
Bob Lantz
2010-03-03 13:52:50 -08:00
parent 04fa18dc61
commit efc9a01cf1
4 changed files with 138 additions and 128 deletions
+41 -27
View File
@@ -6,35 +6,49 @@ This is more complicated than using the higher-level classes,
but it exposes the configuration details and allows customization.
"""
from mininet.mininet import init, Node, createLink
import logging
from mininet.net import init
from mininet.node import Node
from mininet.util import createLink
from mininet.log import info
# print out info() messages, including cmdPrint
logging.LOGLEVELDEFAULT = logging.INFO
def scratchNet( cname='controller', cargs='ptcp:'):
# Create Network
controller = Node( 'c0', inNamespace=False )
switch = Node( 's0', inNamespace=False )
h0 = Node( 'h0' )
h1 = Node( 'h1' )
createLink( h0, switch )
createLink( h1, switch )
# Configure hosts
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' )
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' )
# Start network using kernel datapath
controller.cmdPrint( cname + ' ' + cargs + '&' )
switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'dpctl adddp nl:0' )
for intf in switch.intfs:
# Create Network
print "*** creating Nodes"
controller = Node( 'c0', inNamespace=False )
switch = Node( 's0', inNamespace=False )
h0 = Node( 'h0' )
h1 = Node( 'h1' )
print "*** creating links"
createLink( node1=h0, port1=0, node2=switch, port2=0 )
createLink( node1=h1, port1=0, node2=switch, port2=1 )
# Configure hosts
print "*** configuring hosts"
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' )
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' )
print h0
print h1
# Start network using kernel datapath
controller.cmdPrint( cname + ' ' + cargs + '&' )
switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'dpctl adddp nl:0' )
for intf in switch.intfs.values():
switch.cmdPrint( 'dpctl addif nl:0 ' + intf )
switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &')
# Run test
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
# Stop network
controller.cmdPrint( 'kill %' + cname)
switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'kill %ofprotocol' )
switch.stop()
controller.stop()
switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &')
# Run test
print h0.cmd( 'ping -c1 ' + h1.IP() )
# Stop network
controller.cmdPrint( 'kill %' + cname)
switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'kill %ofprotocol' )
switch.stop()
controller.stop()
if __name__ == '__main__':
init()
scratchNet()
info( '*** Scratch network demo\n' )
init()
scratchNet()
+11 -40
View File
@@ -81,10 +81,10 @@ import signal
from time import sleep
from mininet.cli import CLI
from mininet.log import info, error
from mininet.log import info, error, debug
from mininet.node import KernelSwitch, OVSKernelSwitch
from mininet.util import quietRun, fixLimits
from mininet.util import makeIntfPair, moveIntf, macColonHex
from mininet.util import createLink, macColonHex
from mininet.xterm import cleanUpScreens, makeXterms
DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ]
@@ -150,8 +150,6 @@ class Mininet( object ):
defaultIp: default IP address for intf 0
returns: added host"""
host = self.host( name )
# for now, assume one interface per host.
host.intfs.append( name + '-eth0' )
self.hosts.append( host )
self.nameToNode[ name ] = host
# May wish to add this to actual object
@@ -175,31 +173,6 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def addLink( self, src, srcPort, dst, dstPort ):
"""Add link.
src: source Node
srcPort: source port
dst: destination Node
dstPort: destination port"""
srcIntf = src.intfName( srcPort )
dstIntf = dst.intfName( dstPort )
makeIntfPair( srcIntf, dstIntf )
src.intfs.append( srcIntf )
dst.intfs.append( dstIntf )
src.ports[ srcPort ] = srcIntf
dst.ports[ dstPort ] = dstIntf
#info( '\n' )
#info( 'added intf %s to src node %x\n' % ( srcIntf, src ) )
#info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) )
if src.inNamespace:
#info( 'moving src w/inNamespace set\n' )
moveIntf( srcIntf, src )
if dst.inNamespace:
#info( 'moving dst w/inNamespace set\n' )
moveIntf( dstIntf, dst )
src.connection[ srcIntf ] = ( dst, dstIntf )
dst.connection[ dstIntf ] = ( src, srcIntf )
def addController( self, controller ):
"""Add controller.
controller: Controller class"""
@@ -315,7 +288,7 @@ class Mininet( object ):
for srcId, dstId in sorted( topo.edges() ):
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
srcPort, dstPort = topo.port( srcId, dstId )
self.addLink( src, srcPort, dst, dstPort )
createLink( src, srcPort, dst, dstPort )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
@@ -368,7 +341,7 @@ class Mininet( object ):
controller.start()
info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name )
info( switch.name + ' ')
switch.start( self.controllers )
info( '\n' )
@@ -474,13 +447,14 @@ class Mininet( object ):
l4Type: string, one of [ TCP, UDP ]
verbose: verbose printing
returns: results two-element array of server and client speeds"""
log = info if verbose else debug
if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
host0, host1 = hosts
info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
info( "%s and %s\n" % ( host0.name, host1.name ) )
log( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
log( "%s and %s\n" % ( host0.name, host1.name ) )
host0.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf '
bwArgs = ''
@@ -490,19 +464,16 @@ class Mininet( object ):
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
server = host0.cmd( iperfArgs + '-s &' )
if verbose:
info( '%s\n' % server )
log( '%s\n' % server )
client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' +
bwArgs )
if verbose:
info( '%s\n' % client )
log( '%s\n' % client )
server = host0.cmd( 'killall -9 iperf' )
if verbose:
info( '%s\n' % server )
log( '%s\n' % server )
result = [ self._parseIperf( server ), self._parseIperf( client ) ]
if l4Type == 'UDP':
result.insert( 0, udpBw )
info( '*** Results: %s\n' % result )
log( '*** Results: %s\n' % result )
return result
def iperfUdp( self, udpBw='10M' ):
+71 -48
View File
@@ -40,8 +40,8 @@ import os
import signal
import select
from mininet.log import info, error
from mininet.util import quietRun
from mininet.log import info, error, debug
from mininet.util import quietRun, moveIntf
class Node( object ):
@@ -72,13 +72,13 @@ class Node( object ):
self.inToNode[ self.stdin.fileno() ] = self
self.pid = self.shell.pid
self.intfCount = 0
self.intfs = [] # list of interface names, as strings
self.intfs = {} # dict of port numbers to interface names
self.ports = {} # dict of interface names to port numbers
# replace with Port objects, eventually ?
self.ips = {} # dict of interfaces to ip addresses as strings
self.connection = {} # remote node connected to each interface
self.waiting = False
self.execed = False
self.ports = {} # dict of ints to interface strings
# replace with Port object, eventually
@classmethod
def fdToNode( cls, fd ):
@@ -145,11 +145,13 @@ class Node( object ):
"Send ^C, hopefully interrupting an interactive subprocess."
self.write( chr( 3 ) )
def waitOutput( self ):
def waitOutput( self, verbose=False ):
"""Wait for a command to complete.
Completion is signaled by a sentinel character, ASCII(127)
appearing in the output stream. Wait for the sentinel and return
the output, including trailing newline."""
the output, including trailing newline.
verbose: print output interactively"""
log = info if verbose else debug
assert self.waiting
output = ''
while True:
@@ -157,38 +159,60 @@ class Node( object ):
data = self.read( 1024 )
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
output += data[ :-1 ]
log( output )
break
else:
output += data
self.waiting = False
return output
def cmd( self, cmd ):
def cmd( self, cmd, verbose=False ):
"""Send a command, wait for output, and return it.
cmd: string"""
log = info if verbose else debug
log( '*** %s : %s', self.name, cmd )
self.sendCmd( cmd )
return self.waitOutput()
return self.waitOutput( verbose )
def cmdPrint( self, cmd ):
"""Call cmd and printing its output
cmd: string"""
#info( '*** %s : %s', self.name, cmd )
result = self.cmd( cmd )
#info( '%s\n', result )
return result
return self.cmd( cmd, verbose=True )
# Interface management, configuration, and routing
# BL notes: This might be a bit redundant or over-complicated.
# However, it does allow a bit of specialization, including
# changing the canonical interface names. It's also tricky since
# the real interfaces are created as veth pairs, so we can't
# make a single interface at a time.
def intfName( self, n ):
"Construct a canonical interface name node-intf for interface N."
"Construct a canonical interface name node-ethN for interface n."
return self.name + '-eth' + repr( n )
def newIntf( self ):
"Reserve and return a new interface name."
intfName = self.intfName( self.intfCount )
self.intfCount += 1
self.intfs += [ intfName ]
return intfName
def addIntf( self, intf, port ):
"""Add an interface.
intf: interface name (nodeN-ethM)
port: port number (typically OpenFlow port number)"""
self.intfs[ port ] = intf
self.ports[ intf ] = port
#info( '\n' )
#info( 'added intf %s to node %x\n' % ( srcIntf, src ) )
if self.inNamespace:
#info( 'moving w/inNamespace set\n' )
moveIntf( intf, self )
def connect( self, intf, dstNode, dstIntf ):
"Register connection of intf to dstIntf on dstNode."
self.connection[ intf ] = ( dstNode, dstIntf )
def setMAC( self, intf, mac ):
"""Set the MAC address for an interface.
mac: MAC address as string"""
@@ -206,9 +230,9 @@ class Node( object ):
def setIP( self, intf, ip, bits ):
"""Set the IP address for an interface.
intf: string, interface name
intf: interface name
ip: IP address as a string
bits:"""
bits: prefix length of form /24"""
result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
self.ips[ intf ] = ip
return result
@@ -226,20 +250,19 @@ class Node( object ):
return self.cmd( 'route add default ' + intf )
def IP( self ):
"Return IP address of first interface"
if len( self.intfs ) > 0:
return self.ips.get( self.intfs[ 0 ], None )
"Return IP address of interface 0"
return self.ips.get( self.intfs.get( 0 , None ), None )
def intfIsUp( self ):
"Check if one of our interfaces is up."
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
def intfIsUp( self, port ):
"""Check if interface for a given port number is up.
port: port number"""
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ port ] )
# Other methods
def __str__( self ):
result = self.name + ':'
if self.IP():
result += ' IP=' + self.IP()
result += ' intfs=' + ','.join( self.intfs )
result += ' IP=' + repr( self.IP() )
result += ' intfs=' + ','.join( sorted( self.intfs.values() ) )
result += ' waiting=' + repr( self.waiting )
return result
@@ -282,17 +305,17 @@ class UserSwitch( Switch ):
def start( self, controllers ):
"""Start OpenFlow reference user datapath.
Log to /tmp/sN-{ofd,ofp}.log.
controllers: dict of controller names to objects"""
controllers: list of controller objects"""
controller = controllers[ 0 ]
ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd( 'ifconfig lo up' )
intfs = self.intfs
intfs = sorted( self.intfs.values() )
self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) +
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
' punix:/tmp/' + self.name +
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name +
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
' tcp:' + controller.IP() + ' --fail=closed' +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
@@ -322,20 +345,20 @@ class KernelSwitch( Switch ):
# Delete local datapath if it exists;
# then create a new one monitoring the given interfaces
quietRun( 'dpctl deldp nl:%i' % self.dp )
self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
self.cmd( 'dpctl adddp nl:%i' % self.dp )
if self.defaultMac:
intf = 'of%i' % self.dp
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1:
if len( self.intfs ) != max( self.intfs ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports )
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
'supported: %s' % self.intfs )
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
self.cmd( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
controller = controllers[ 0 ]
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controller.IP() + ':' +
str( controller.port ) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
@@ -345,11 +368,11 @@ class KernelSwitch( Switch ):
"Terminate kernel datapath."
quietRun( 'dpctl deldp nl:%i' % self.dp )
# In theory the interfaces should go away after we shut down.
# However, this takes time, so we're better off to remove them
# However, this takes time, so we're better off removing them
# explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow.
self.cmd( 'kill %ofprotocol' )
for intf in self.intfs:
for intf in self.intfs.values():
quietRun( 'ip link del ' + intf )
info( '.' )
@@ -374,21 +397,21 @@ class OVSKernelSwitch( Switch ):
# Delete local datapath if it exists;
# then create a new one monitoring the given interfaces
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
if self.defaultMac:
intf = 'dp' % self.dp
mac = self.defaultMac
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1:
if len( self.intfs ) != max( self.intfs ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports )
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
'supported: %s' % self.intfs )
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
self.cmd( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
controller = controllers[ 0 ]
self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
controller.IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False
@@ -397,11 +420,11 @@ class OVSKernelSwitch( Switch ):
"Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
# In theory the interfaces should go away after we shut down.
# However, this takes time, so we're better off to remove them
# However, this takes time, so we're better off removing them
# explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow.
self.cmd( 'kill %ovs-openflowd' )
for intf in self.intfs:
for intf in self.intfs.values():
quietRun( 'ip link del ' + intf )
info( '.' )
@@ -425,8 +448,8 @@ class Controller( Node ):
Log to /tmp/cN.log"""
cout = '/tmp/' + self.name + '.log'
if self.cdir is not None:
self.cmdPrint( 'cd ' + self.cdir )
self.cmdPrint( self.controller + ' ' + self.cargs +
self.cmd( 'cd ' + self.cdir )
self.cmd( self.controller + ' ' + self.cargs +
' 1> ' + cout + ' 2> ' + cout + ' &' )
self.execed = False
+15 -13
View File
@@ -15,7 +15,7 @@ def run( cmd ):
def checkRun( cmd ):
"""Simple interface to subprocess.check_call()
cmd: list of command params"""
check_call( cmd.split( ' ' ) )
return check_call( cmd.split( ' ' ) )
def quietRun( cmd ):
"""Run a command, routing stderr to stdout, and return the output.
@@ -63,7 +63,7 @@ def makeIntfPair( intf1, intf2 ):
quietRun( 'ip link del ' + intf2 )
# Create new pair
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
return checkRun( cmd )
return quietRun( cmd )
def retry( retries, delaySecs, fn, *args, **keywords ):
"""Try something several times before giving up.
@@ -101,19 +101,20 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
def createLink( node1, node2, retries=10, delaySecs=0.001 ):
def createLink( node1, port1, node2, port2 ):
"""Create a link between nodes, making an interface for each.
node1: Node object
node2: Node object"""
intf1 = node1.newIntf()
intf2 = node2.newIntf()
port1: node1 port number
node2: Node object
port2: node2 port number
returns: intf1 name, intf2 name"""
intf1 = node1.intfName( port1 )
intf2 = node2.intfName( port2 )
makeIntfPair( intf1, intf2 )
if node1.inNamespace:
retry( retries, delaySecs, moveIntf, intf1, node1 )
if node2.inNamespace:
retry( retries, delaySecs, moveIntf, intf2, node2 )
node1.connection[ intf1 ] = ( node2, intf2 )
node2.connection[ intf2 ] = ( node1, intf1 )
node1.addIntf( intf1, port1 )
node2.addIntf( intf2, port2 )
node1.connect( intf1, node2, intf2 )
node2.connect( intf2, node1, intf1 )
return intf1, intf2
def fixLimits():
@@ -141,7 +142,8 @@ def macColonHex( mac ):
def ipStr( ip ):
"""Generate IP address string
returns: ip addr string"""
ip: unsigned int of form x << 16 | y << 8 | z
returns: ip address string 10.x.y.z """
hi = ( ip & 0xff0000 ) >> 16
mid = ( ip & 0xff00 ) >> 8
lo = ip & 0xff