Made it possible to create a Mininet() without using a Topo object.

This increases flexibility by allowing a topology to be manually created
in Python, or specified using another format, without having to create
a Topo object first.

However, Topos are useful, and are still the default topology object!
This commit is contained in:
Bob Lantz
2010-02-26 17:16:07 -08:00
parent 3e021c2579
commit 019bff8282
3 changed files with 136 additions and 142 deletions
Regular → Executable
View File
+115 -117
View File
@@ -84,7 +84,7 @@ from mininet.cli import CLI
from mininet.log import info, error
from mininet.node import KernelSwitch, OVSKernelSwitch
from mininet.util import quietRun, fixLimits
from mininet.util import makeIntfPair, moveIntf
from mininet.util import makeIntfPair, moveIntf, macColonHex
from mininet.xterm import cleanUpScreens, makeXterms
DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ]
@@ -110,92 +110,103 @@ class Mininet( object ):
inNamespace=False,
autoSetMacs=False, autoStaticArp=False ):
"""Create Mininet object.
topo: Topo object
topo: Topo (topology) object or None
switch: Switch class
host: Host class
controller: Controller class
cparams: ControllerParams object
now: build now?
now: build now from topo?
xterms: if build now, spawn xterms?
cleanup: if build now, cleanup before creating?
inNamespace: spawn switches and controller in net namespaces?
autoSetMacs: set MAC addrs to DPIDs?
autoStaticArp: set all-pairs static MAC addrs?"""
self.topo = topo
self.switch = switch
self.host = host
self.controller = controller
self.cparams = cparams
self.nodes = {} # dpid to Node{ Host, Switch } objects
self.controllers = {} # controller name to Controller objects
self.dps = 0 # number of created kernel datapaths
self.topo = topo
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
self.autoSetMacs = autoSetMacs
self.autoStaticArp = autoStaticArp
self.hosts = []
self.switches = []
self.controllers = []
self.nameToNode = {} # name to Node (Host/Switch) objects
self.idToNode = {} # dpid to Node (Host/Switch) objects
self.dps = 0 # number of created kernel datapaths
self.terms = [] # list of spawned xterm processes
if build:
self.build()
if topo and build:
self.buildFromTopo( self.topo )
def _addHost( self, dpid ):
def addHost( self, name, defaultMac=None, defaultIp=None ):
"""Add host.
dpid: DPID of host to add"""
host = self.host( 'h' + self.topo.name( dpid ) )
name: name of host to add
defaultMac: default MAC address for intf 0
defaultIp: default IP address for intf 0
returns: added host"""
host = self.host( name )
# for now, assume one interface per host.
host.intfs.append( 'h' + self.topo.name( dpid ) + '-eth0' )
self.nodes[ dpid ] = host
#info( '%s ' % host.name )
host.intfs.append( name + '-eth0' )
self.hosts.append( host )
self.nameToNode[ name ] = host
# May wish to add this to actual object
if defaultMac:
host.defaultMac = defaultMac
if defaultIp:
host.defaultIP = defaultIp
return host
def _addSwitch( self, dpid ):
def addSwitch( self, name, defaultMac=None ):
"""Add switch.
dpid: DPID of switch to add"""
sw = None
swDpid = None
if self.autoSetMacs:
swDpid = dpid
name: name of switch to add
defaultMac: default MAC address for kernel/OVS switch intf 0
returns: added switch"""
if self.switch is KernelSwitch or self.switch is OVSKernelSwitch:
sw = self.switch( 's_' + self.topo.name( dpid ), dp = self.dps,
dpid = swDpid )
sw = self.switch( name, dp=self.dps, defaultMac=defaultMac )
self.dps += 1
else:
sw = self.switch( 's_' + self.topo.name( dpid ) )
self.nodes[ dpid ] = sw
sw = self.switch( name )
self.switches.append( sw )
self.nameToNode[ name ] = sw
return sw
def _addLink( self, src, dst ):
def addLink( self, src, srcPort, dst, dstPort ):
"""Add link.
src: source DPID
dst: destination DPID"""
srcPort, dstPort = self.topo.port( src, dst )
srcNode = self.nodes[ src ]
dstNode = self.nodes[ dst ]
srcIntf = srcNode.intfName( srcPort )
dstIntf = dstNode.intfName( dstPort )
src: source Node
srcPort: source port
dst: destination Node
dstPort: destination port"""
srcIntf = src.intfName( srcPort )
dstIntf = dst.intfName( dstPort )
makeIntfPair( srcIntf, dstIntf )
srcNode.intfs.append( srcIntf )
dstNode.intfs.append( dstIntf )
srcNode.ports[ srcPort ] = srcIntf
dstNode.ports[ dstPort ] = 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 srcNode.inNamespace:
if src.inNamespace:
#info( 'moving src w/inNamespace set\n' )
moveIntf( srcIntf, srcNode )
if dstNode.inNamespace:
moveIntf( srcIntf, src )
if dst.inNamespace:
#info( 'moving dst w/inNamespace set\n' )
moveIntf( dstIntf, dstNode )
srcNode.connection[ srcIntf ] = ( dstNode, dstIntf )
dstNode.connection[ dstIntf ] = ( srcNode, srcIntf )
moveIntf( dstIntf, dst )
src.connection[ srcIntf ] = ( dst, dstIntf )
dst.connection[ dstIntf ] = ( src, srcIntf )
def _addController( self, controller ):
def addController( self, controller ):
"""Add controller.
controller: Controller class"""
controller = self.controller( 'c0', self.inNamespace )
if controller: # allow controller-less setups
self.controllers[ 'c0' ] = controller
self.controllers.append( controller )
self.nameToNode[ 'c0' ] = controller
# Control network support:
#
@@ -216,7 +227,7 @@ class Mininet( object ):
#
# 4. Even if we dispense with this in general, it could still be
# useful for people who wish to simulate a separate control
# network ( since real networks may need one! )
# network (since real networks may need one!)
def _configureControlNetwork( self ):
"Configure control network."
@@ -229,12 +240,11 @@ class Mininet( object ):
"""
# params were: controller, switches, ips
controller = self.controllers[ 'c0' ]
controller = self.controllers[ 0 ]
info( '%s <-> ' % controller.name )
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
for switch in self.switches:
info( '%s ' % switch.name )
sip = self.topo.ip( switchDpid )#ips.next()
sip = switch.defaultIP
sintf = switch.intfs[ 0 ]
node, cintf = switch.connection[ sintf ]
if node != controller:
@@ -253,8 +263,7 @@ class Mininet( object ):
info( '*** Waiting for %s to come up\n',
controller.intfs[ 0 ] )
sleep( 1 )
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
for switch in self.switches:
while not switch.intfIsUp( switch.intfs[ 0 ] ):
info( '*** Waiting for %s to come up\n' %
switch.intfs[ 0 ] )
@@ -267,10 +276,9 @@ class Mininet( object ):
def _configHosts( self ):
"Configure a set of hosts."
# params were: hosts, ips
for hostDpid in self.topo.hosts():
host = self.nodes[ hostDpid ]
for host in self.hosts:
hintf = host.intfs[ 0 ]
host.setIP( hintf, self.topo.ip( hostDpid ),
host.setIP( hintf, host.defaultIP,
'/' + str( self.cparams.subnetSize ) )
host.setDefaultRoute( hintf )
# You're low priority, dude!
@@ -278,28 +286,37 @@ class Mininet( object ):
info( '%s ', host.name )
info( '\n' )
def build( self ):
"""Build mininet.
def buildFromTopo( self, topo ):
"""Build mininet from a topology object
At the end of this function, everything should be connected
and up."""
if self.cleanup:
pass # cleanup
# validate topo?
info( '*** Adding controller\n' )
self._addController( self.controller )
self.addController( self.controller )
info( '*** Creating network\n' )
info( '*** Adding hosts:\n' )
for host in sorted( self.topo.hosts() ):
self._addHost( host )
info( '0x%x ' % host )
for hostId in sorted( topo.hosts() ):
name = 'h' + topo.name( hostId )
mac = macColonHex( hostId ) if self.setMacs else None
ip = topo.ip( hostId )
host = self.addHost( name, defaultIp=ip, defaultMac=mac )
self.idToNode[ hostId ] = host
info( name )
info( '\n*** Adding switches:\n' )
for switch in sorted( self.topo.switches() ):
self._addSwitch( switch )
info( '0x%x ' % switch )
for switchId in sorted( topo.switches() ):
name = 's' + topo.name( switchId )
mac = macColonHex( switchId) if self.setMacs else None
switch = self.addSwitch( name, defaultMac=mac )
self.idToNode[ switchId ] = switch
info( name )
info( '\n*** Adding edges:\n' )
for src, dst in sorted( self.topo.edges() ):
self._addLink( src, dst )
info( '(0x%x, 0x%x) ' % ( src, dst ) )
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 )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
if self.inNamespace:
@@ -316,21 +333,13 @@ class Mininet( object ):
if self.autoStaticArp:
self.staticArp()
def switchNodes( self ):
"Return switch nodes."
return [ self.nodes[ dpid ] for dpid in self.topo.switches() ]
def hostNodes( self ):
"Return host nodes."
return [ self.nodes[ dpid ] for dpid in self.topo.hosts() ]
def startXterms( self ):
"Start an xterm for each node in the topo."
"Start an xterm for each node."
info( "*** Running xterms on %s\n" % os.environ[ 'DISPLAY' ] )
cleanUpScreens()
self.terms += makeXterms( self.controllers.values(), 'controller' )
self.terms += makeXterms( self.switchNodes(), 'switch' )
self.terms += makeXterms( self.hostNodes(), 'host' )
self.terms += makeXterms( self.controllers, 'controller' )
self.terms += makeXterms( self.switches, 'switch' )
self.terms += makeXterms( self.hosts, 'host' )
def stopXterms( self ):
"Kill each xterm."
@@ -342,28 +351,24 @@ class Mininet( object ):
def setMacs( self ):
"""Set MAC addrs to correspond to datapath IDs on hosts.
Assume that the host only has one interface."""
for dpid in self.topo.hosts():
hostNode = self.nodes[ dpid ]
hostNode.setMAC( hostNode.intfs[ 0 ], dpid )
for host in self.hosts:
host.setMAC( host.intfs[ 0 ], host.defaultMac )
def staticArp( self ):
"Add all-pairs ARP entries to remove the need to handle broadcast."
for src in self.topo.hosts():
srcNode = self.nodes[ src ]
for dst in self.topo.hosts():
for src in self.hosts:
for dst in self.hosts:
if src != dst:
srcNode.setARP( dst, dst )
src.setARP( ip=dst.IP(), mac=dst.defaultMac )
def start( self ):
"Start controller and switches"
info( '*** Starting controller\n' )
for cnode in self.controllers.values():
cnode.start()
info( '*** Starting %s switches\n' % len( self.topo.switches() ) )
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
#info( 'switch = %s' % switch )
info( '0x%x ' % switchDpid )
for controller in self.controllers:
controller.start()
info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name )
switch.start( self.controllers )
info( '\n' )
@@ -372,21 +377,19 @@ class Mininet( object ):
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i hosts\n' % len( self.topo.hosts() ) )
for hostDpid in self.topo.hosts():
host = self.nodes[ hostDpid ]
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( '%s ' % host.name )
host.terminate()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.topo.switches() ) )
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
info( '%s' % switch.name )
info( '*** Stopping %i switches\n' % len( self.switches ) )
for switch in self.switches:
info( '%s ' % switch.name )
switch.stop()
info( '\n' )
info( '*** Stopping controller\n' )
for cnode in self.controllers.values():
cnode.stop()
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
controller.stop()
info( '*** Test complete\n' )
def run( self, test, **params ):
@@ -411,7 +414,7 @@ class Mininet( object ):
def ping( self, hosts=None ):
"""Ping between all specified hosts.
hosts: list of host DPIDs
hosts: list of hosts
returns: ploss packet loss percentage"""
#self.start()
# check if running - only then, start?
@@ -419,13 +422,11 @@ class Mininet( object ):
lost = 0
ploss = None
if not hosts:
hosts = self.topo.hosts()
info( '*** Ping: testing ping reachability\n' )
for nodeDpid in hosts:
node = self.nodes[ nodeDpid ]
hosts = self.hosts
info( '*** Ping: testing ping reachability\n' )
for node in hosts:
info( '%s -> ' % node.name )
for destDpid in hosts:
dest = self.nodes[ destDpid ]
for dest in hosts:
if node != dest:
result = node.cmd( 'ping -c1 ' + dest.IP() )
sent, received = self._parsePing( result )
@@ -451,8 +452,7 @@ class Mininet( object ):
def pingPair( self ):
"""Ping between first two hosts, useful for testing.
returns: ploss packet loss percentage"""
hostsSorted = sorted( self.topo.hosts() )
hosts = [ hostsSorted[ 0 ], hostsSorted[ 1 ] ]
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
return self.ping( hosts=hosts )
@staticmethod
@@ -470,17 +470,15 @@ class Mininet( object ):
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M',
verbose=False ):
"""Run iperf between two hosts.
hosts: list of host DPIDs; if None, uses opposite hosts
hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ]
verbose: verbose printing
returns: results two-element array of server and client speeds"""
if not hosts:
hostsSorted = sorted( self.topo.hosts() )
hosts = [ hostsSorted[ 0 ], hostsSorted[ -1 ] ]
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
host0 = self.nodes[ hosts[ 0 ] ]
host1 = self.nodes[ hosts[ 1 ] ]
host0, host1 = hosts
info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
info( "%s and %s\n" % ( host0.name, host1.name ) )
host0.cmd( 'killall -9 iperf' )
+21 -25
View File
@@ -41,7 +41,7 @@ import signal
import select
from mininet.log import info, error
from mininet.util import quietRun, macColonHex, ipStr
from mininet.util import quietRun
class Node( object ):
@@ -191,19 +191,16 @@ class Node( object ):
def setMAC( self, intf, mac ):
"""Set the MAC address for an interface.
mac: MAC address as unsigned int"""
macStr = macColonHex( mac )
mac: MAC address as string"""
result = self.cmd( [ 'ifconfig', intf, 'down' ] )
result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
result += self.cmd( [ 'ifconfig', intf, 'up' ] )
return result
def setARP( self, ip, mac ):
"""Add an ARP entry.
ip: IP address as unsigned int
mac: MAC address as unsigned int"""
ip = ipStr( ip )
mac = macColonHex( mac )
ip: IP address as string
mac: MAC address as string"""
result = self.cmd( [ 'arp', '-s', ip, mac ] )
return result
@@ -286,9 +283,7 @@ class UserSwitch( Switch ):
"""Start OpenFlow reference user datapath.
Log to /tmp/sN-{ofd,ofp}.log.
controllers: dict of controller names to objects"""
if 'c0' not in controllers:
raise Exception( 'User datapath start() requires controller c0' )
controller = controllers[ 'c0' ]
controller = controllers[ 0 ]
ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd( 'ifconfig lo up' )
@@ -311,14 +306,14 @@ class KernelSwitch( Switch ):
"""Kernel-space switch.
Currently only works in the root namespace."""
def __init__( self, name, dp=None, dpid=None ):
def __init__( self, name, dp=None, defaultMac=None ):
"""Init.
name:
dp: netlink id (0, 1, 2, ...)
dpid: datapath ID as unsigned int; random value if None"""
defaultMac: default MAC as string; random value if None"""
Switch.__init__( self, name, inNamespace=False )
self.dp = dp
self.dpid = dpid
self.defaultMac = defaultMac
def start( self, controllers ):
"Start up reference kernel datapath."
@@ -328,10 +323,9 @@ class KernelSwitch( Switch ):
# then create a new one monitoring the given interfaces
quietRun( 'dpctl deldp nl:%i' % self.dp )
self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
if self.dpid:
if self.defaultMac:
intf = 'of%i' % self.dp
macStr = macColonHex( self.dpid )
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
@@ -340,9 +334,10 @@ class KernelSwitch( Switch ):
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
controller = controllers[ 0 ]
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controllers[ 'c0' ].IP() + ':' +
str( controllers[ 'c0' ].port ) +
controller.IP() + ':' +
str( controller.port ) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False
@@ -363,14 +358,14 @@ class OVSKernelSwitch( Switch ):
"""Open VSwitch kernel-space switch.
Currently only works in the root namespace."""
def __init__( self, name, dp=None, dpid=None ):
def __init__( self, name, dp=None, defaultMac=None ):
"""Init.
name:
dp: netlink id (0, 1, 2, ...)
dpid: datapath ID as unsigned int; random value if None"""
Switch.__init__( self, name, inNamespace=False )
self.dp = dp
self.dpid = dpid
self.defaultMac = defaultMac
def start( self, controllers ):
"Start up kernel datapath."
@@ -380,10 +375,10 @@ class OVSKernelSwitch( Switch ):
# 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 )
if self.dpid:
if self.defaultMac:
intf = 'dp' % self.dp
macStr = macColonHex( self.dpid )
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
mac = self.defaultMac
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
@@ -392,8 +387,9 @@ class OVSKernelSwitch( Switch ):
self.cmdPrint( '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:' +
controllers[ 'c0' ].IP() + ':' +
controller.IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False