Revamp custom topology input
Defining custom topologies, switch types, controllers, and hosts is now much easier. Plus, all Ripcord-specific stuff has been moved out.
This commit is contained in:
@@ -3,18 +3,19 @@
|
|||||||
"""
|
"""
|
||||||
Mininet runner
|
Mininet runner
|
||||||
author: Brandon Heller (brandonh@stanford.edu)
|
author: Brandon Heller (brandonh@stanford.edu)
|
||||||
|
|
||||||
|
To see options:
|
||||||
|
sudo mn -h
|
||||||
|
|
||||||
|
Example to pull custom params (topo, switch, etc.) from a file:
|
||||||
|
sudo mn --custom ~/mininet/custom/custom_example.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os.path
|
import os.path
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
try:
|
|
||||||
from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo
|
|
||||||
USERIPCORD = True
|
|
||||||
except ImportError:
|
|
||||||
USERIPCORD = False
|
|
||||||
|
|
||||||
from mininet.log import lg, LEVELS
|
from mininet.log import lg, LEVELS
|
||||||
from mininet.net import Mininet, init
|
from mininet.net import Mininet, init
|
||||||
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
|
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
|
||||||
@@ -29,16 +30,6 @@ TOPOS = { 'minimal': ( lambda: SingleSwitchTopo( k=2 ) ),
|
|||||||
'single100': ( lambda: SingleSwitchTopo( k=100 ) ),
|
'single100': ( lambda: SingleSwitchTopo( k=100 ) ),
|
||||||
'linear2': ( lambda: LinearTopo( k=2 ) ),
|
'linear2': ( lambda: LinearTopo( k=2 ) ),
|
||||||
'linear100': ( lambda: LinearTopo( k=100 ) ) }
|
'linear100': ( lambda: LinearTopo( k=100 ) ) }
|
||||||
if USERIPCORD:
|
|
||||||
TOPOSRIPCORD = {
|
|
||||||
'tree16': ( lambda: TreeTopo( depth=3, fanout=4 ) ),
|
|
||||||
'tree64': ( lambda: TreeTopo( depth=4, fanout=4 ) ),
|
|
||||||
'tree1024': ( lambda: TreeTopo( depth=3, fanout=32 ) ),
|
|
||||||
'fattree4': ( lambda: FatTreeTopo( k=4 ) ),
|
|
||||||
'fattree6': ( lambda: FatTreeTopo( k=6 ) ),
|
|
||||||
'vl2': ( lambda: VL2Topo( da=4, di=4 ) ),
|
|
||||||
'vl2reduced': ( lambda: VL2Topo( da=4, di=4, edgeDown=1 ) ) }
|
|
||||||
TOPOS.update( TOPOSRIPCORD )
|
|
||||||
|
|
||||||
SWITCHDEF = 'kernel'
|
SWITCHDEF = 'kernel'
|
||||||
SWITCHES = { 'kernel': KernelSwitch,
|
SWITCHES = { 'kernel': KernelSwitch,
|
||||||
@@ -57,8 +48,7 @@ CONTROLLERS = { 'ref': Controller,
|
|||||||
'none': lambda a, b: None }
|
'none': lambda a, b: None }
|
||||||
|
|
||||||
# optional tests to run
|
# optional tests to run
|
||||||
TESTS = [ 'cli', 'build', 'pingAll', 'pingPair', 'iperf', 'all',
|
TESTS = [ 'cli', 'build', 'pingAll', 'pingPair', 'iperf', 'all', 'iperfUdp' ]
|
||||||
'iperfUdp' ]
|
|
||||||
|
|
||||||
|
|
||||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||||
@@ -86,14 +76,37 @@ class MininetRunner( object ):
|
|||||||
def __init__( self ):
|
def __init__( self ):
|
||||||
"Init."
|
"Init."
|
||||||
self.options = None
|
self.options = None
|
||||||
|
self.validate = None
|
||||||
|
|
||||||
self.parseArgs()
|
self.parseArgs()
|
||||||
self.setup()
|
self.setup()
|
||||||
self.begin()
|
self.begin()
|
||||||
|
|
||||||
|
def parseCustomFile( self, custom ):
|
||||||
|
"Parse custom file and add params before parsing cmd-line options."
|
||||||
|
if os.path.isfile( custom ):
|
||||||
|
execfile( custom, globals(), globals() )
|
||||||
|
if 'topos' in globals(): TOPOS.update( topos )
|
||||||
|
if 'switches' in globals(): SWITCHES.update( switches )
|
||||||
|
if 'hosts' in globals(): HOSTS.update( hosts )
|
||||||
|
if 'controllers' in globals(): CONTROLLERS.update( controllers )
|
||||||
|
if 'validate' in globals(): self.validate = validate
|
||||||
|
else:
|
||||||
|
raise Exception( 'could not find custom file: %s' % custom )
|
||||||
|
|
||||||
def parseArgs( self ):
|
def parseArgs( self ):
|
||||||
"""Parse command-line args and return options object.
|
"""Parse command-line args and return options object.
|
||||||
returns: opts parse options dict"""
|
returns: opts parse options dict"""
|
||||||
|
if '--custom' in sys.argv:
|
||||||
|
print "custom in sys.argv"
|
||||||
|
index = sys.argv.index( '--custom' )
|
||||||
|
if len( sys.argv ) > index + 1:
|
||||||
|
custom = sys.argv[ index + 1 ]
|
||||||
|
print "custom = %s" % custom
|
||||||
|
self.parseCustomFile( custom )
|
||||||
|
else:
|
||||||
|
raise Exception( 'Custom file name not found' )
|
||||||
|
|
||||||
opts = OptionParser()
|
opts = OptionParser()
|
||||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||||
@@ -101,7 +114,7 @@ class MininetRunner( object ):
|
|||||||
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||||
|
|
||||||
opts.add_option( '--custom', type='string', default=None,
|
opts.add_option( '--custom', type='string', default=None,
|
||||||
help='read custom mininet from current dir' )
|
help='read custom topo and node params from .py file' )
|
||||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||||
default=TESTS[ 0 ],
|
default=TESTS[ 0 ],
|
||||||
help='[' + ' '.join( TESTS ) + ']' )
|
help='[' + ' '.join( TESTS ) + ']' )
|
||||||
@@ -133,18 +146,6 @@ class MininetRunner( object ):
|
|||||||
# validate environment setup
|
# validate environment setup
|
||||||
init()
|
init()
|
||||||
|
|
||||||
# check for invalid combinations
|
|
||||||
if ( self.options.controller == 'ref' and
|
|
||||||
( ( 'fattree' in self.options.topo ) or
|
|
||||||
( 'vl2' in self.options.topo ) ) ):
|
|
||||||
raise Exception( 'multipath topos require multipath-capable '
|
|
||||||
'controller.' )
|
|
||||||
|
|
||||||
if self.options.custom:
|
|
||||||
if not os.path.isfile( self.options.custom ):
|
|
||||||
raise Exception( 'could not find custom file: %s' %
|
|
||||||
self.options.custom )
|
|
||||||
|
|
||||||
def begin( self ):
|
def begin( self ):
|
||||||
"Create and run mininet."
|
"Create and run mininet."
|
||||||
|
|
||||||
@@ -159,25 +160,18 @@ class MininetRunner( object ):
|
|||||||
ipAddress=self.options.ip,
|
ipAddress=self.options.ip,
|
||||||
port=self.options.port )
|
port=self.options.port )
|
||||||
|
|
||||||
|
if self.validate:
|
||||||
|
self.validate(self.options)
|
||||||
|
|
||||||
controllerParams = ControllerParams( 0x0a000000, 8 ) # 10.0.0.0/8
|
controllerParams = ControllerParams( 0x0a000000, 8 ) # 10.0.0.0/8
|
||||||
inNamespace = self.options.inNamespace
|
inNamespace = self.options.inNamespace
|
||||||
xterms = self.options.xterms
|
xterms = self.options.xterms
|
||||||
mac = self.options.mac
|
mac = self.options.mac
|
||||||
arp = self.options.arp
|
arp = self.options.arp
|
||||||
mn = None
|
mn = Mininet( topo, switch, host, controller, controllerParams,
|
||||||
if not self.options.custom:
|
inNamespace=inNamespace,
|
||||||
mn = Mininet( topo, switch, host, controller, controllerParams,
|
xterms=xterms, autoSetMacs=mac,
|
||||||
inNamespace=inNamespace,
|
autoStaticArp=arp )
|
||||||
xterms=xterms, autoSetMacs=mac,
|
|
||||||
autoStaticArp=arp )
|
|
||||||
else:
|
|
||||||
globals_ = {}
|
|
||||||
locals_ = {}
|
|
||||||
execfile( self.options.custom, globals_, locals_ )
|
|
||||||
if 'mn' not in locals_:
|
|
||||||
raise Exception( 'could not find mn var in custom file' )
|
|
||||||
else:
|
|
||||||
mn = locals_[ 'mn' ]
|
|
||||||
|
|
||||||
test = self.options.test
|
test = self.options.test
|
||||||
if test != 'build':
|
if test != 'build':
|
||||||
|
|||||||
+39
-19
@@ -1,25 +1,45 @@
|
|||||||
'''Example of custom topo
|
"""Custom topology example
|
||||||
|
|
||||||
@author Brandon Heller (brandonh@stanford.edu)
|
author: Brandon Heller (brandonh@stanford.edu)
|
||||||
|
|
||||||
'''
|
Two directly connected switches plus a host for each switch:
|
||||||
|
|
||||||
from mininet.topo import SingleSwitchTopo
|
host --- switch --- switch --- host
|
||||||
from mininet.net import Mininet
|
|
||||||
from mininet.node import KernelSwitch, Host, Controller, ControllerParams
|
|
||||||
|
|
||||||
topo = SingleSwitchTopo(k = 2) # build topology object
|
Adding the 'topos' dict with a key/value pair to generate our newly defined
|
||||||
switch = KernelSwitch
|
topology enables one to pass in '--topo=mytopo' from the command line.
|
||||||
host = Host
|
"""
|
||||||
controller = Controller
|
|
||||||
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
|
|
||||||
in_namespace = False
|
|
||||||
xterms = False
|
|
||||||
mac = True
|
|
||||||
arp = True
|
|
||||||
|
|
||||||
mn = Mininet(topo, switch, host, controller, controller_params,
|
from mininet.topo import Topo, Node
|
||||||
in_namespace = in_namespace,
|
|
||||||
xterms = xterms, auto_set_macs = mac,
|
|
||||||
auto_static_arp = arp)
|
|
||||||
|
|
||||||
|
class MyTopo( Topo ):
|
||||||
|
"""Simple topology example."""
|
||||||
|
|
||||||
|
def __init__( self, enable_all = True ):
|
||||||
|
"""Create custom topo."""
|
||||||
|
|
||||||
|
# Add default members to class.
|
||||||
|
super( MyTopo, self ).__init__()
|
||||||
|
|
||||||
|
# Set Node IDs for hosts and switches
|
||||||
|
leftHost = 1
|
||||||
|
leftSwitch = 2
|
||||||
|
rightSwitch = 3
|
||||||
|
rightHost = 4
|
||||||
|
|
||||||
|
# Add nodes
|
||||||
|
self._add_node( leftSwitch, Node( is_switch=True ) )
|
||||||
|
self._add_node( rightSwitch, Node( is_switch=True ) )
|
||||||
|
self._add_node( leftHost, Node( is_switch=False ) )
|
||||||
|
self._add_node( rightHost, Node( is_switch=False ) )
|
||||||
|
|
||||||
|
# Add edges
|
||||||
|
self._add_edge( leftHost, leftSwitch )
|
||||||
|
self._add_edge( leftSwitch, rightSwitch )
|
||||||
|
self._add_edge( rightSwitch, rightHost )
|
||||||
|
|
||||||
|
# Consider all switches and hosts 'on'
|
||||||
|
self.enable_all()
|
||||||
|
|
||||||
|
|
||||||
|
topos = { 'mytopo': ( lambda: MyTopo() ) }
|
||||||
+3
-1
@@ -107,7 +107,7 @@ class Topo(object):
|
|||||||
self.g.add_node(dpid)
|
self.g.add_node(dpid)
|
||||||
self.node_info[dpid] = node
|
self.node_info[dpid] = node
|
||||||
|
|
||||||
def _add_edge(self, src, dst, edge):
|
def _add_edge(self, src, dst, edge = None):
|
||||||
'''Add edge (Node, Node) to graph.
|
'''Add edge (Node, Node) to graph.
|
||||||
|
|
||||||
@param src src dpid
|
@param src src dpid
|
||||||
@@ -116,6 +116,8 @@ class Topo(object):
|
|||||||
'''
|
'''
|
||||||
src, dst = tuple(sorted([src, dst]))
|
src, dst = tuple(sorted([src, dst]))
|
||||||
self.g.add_edge(src, dst)
|
self.g.add_edge(src, dst)
|
||||||
|
if not edge:
|
||||||
|
edge = Edge()
|
||||||
self.edge_info[(src, dst)] = edge
|
self.edge_info[(src, dst)] = edge
|
||||||
self._add_port(src, dst)
|
self._add_port(src, dst)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user