Skip to content
Snippets Groups Projects
Commit 019bff82 authored by Bob Lantz's avatar Bob Lantz
Browse files

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!
parent 3e021c25
No related branches found
No related tags found
No related merge requests found
bin/mn 100644 → 100755
File mode changed from 100644 to 100755
......@@ -84,7 +84,7 @@
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 @@ def __init__( self, topo, switch, host, controller, cparams,
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 )
def _addSwitch( self, dpid ):
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, 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 @@ def _addController( self, controller ):
#
# 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 @@ def _configureRoutedControlNetwork( self ):
"""
# 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 @@ def _configureRoutedControlNetwork( self ):
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 @@ def _configureRoutedControlNetwork( self ):
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 @@ def _configHosts( self ):
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 @@ def build( self ):
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 @@ def stopXterms( self ):
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 @@ def stop( self ):
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 @@ def _parsePing( pingOutput ):
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 @@ def ping( self, hosts=None ):
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 @@ def pingAll( self ):
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 @@ def _parseIperf( iperfOutput ):
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' )
......
......@@ -41,7 +41,7 @@
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 @@ def newIntf( self ):
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 @@ def start( self, controllers ):
"""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 @@ def start( self, controllers ):
# 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 @@ def start( self, controllers ):
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 @@ def start( self, controllers ):
# 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 @@ def start( self, controllers ):
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment