Move TreeNet to new Mininet API

Also remove all non-object-oriented legacy Mininet code and update
tests.

User-space compatibility is untested, but most of the code for it is
still in.
This commit is contained in:
Brandon Heller
2009-12-26 14:25:48 -08:00
parent e3621eb057
commit 8b5062a3a1
5 changed files with 670 additions and 976 deletions
+5 -472
View File
@@ -1,483 +1,16 @@
#!/usr/bin/python
'''Run a FatTree network from the Ripcord project.
For verbose printout, set LOG_LEVEL_DEFAULT in mininet.py to logging.INFO.
'''
import os
import re
from subprocess import call
import sys
from time import sleep
'''Run a FatTree network from the Ripcord project.'''
from ripcord.topo import FatTreeTopo
from mininet.net import init
from mininet.node import Switch, Controller, Host
from mininet.logging_mod import lg, set_loglevel
from mininet.util import make_veth_pair, move_intf, retry, quietRun
from mininet.util import MOVEINTF_DELAY
class Mininet(object):
'''Network emulation with hosts spawned in network namespaces.'''
def __init__(self, topo, switch, host, controller, cparams,
build = True, xterms = False, cleanup = False,
in_namespace = False, switch_is_kernel = True):
'''Create Mininet object.
@param topo Topo object
@param switch Switch class
@param host Host class
@param controller Controller class
@param cparams ControllerParams object
@param now build now?
@param xterms if build now, spawn xterms?
@param cleanup if build now, cleanup before creating?
@param in_namespace spawn switches and hosts in their own namespace?
'''
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.in_namespace = in_namespace
self.switch_is_kernel = switch_is_kernel
self.kernel = True #temporary!
if build:
self.build(xterms, cleanup)
def _add_host(self, dpid):
'''Add host.
@param dpid DPID of host to add
'''
host = self.host('h_' + self.topo.name(dpid))
# for now, assume one interface per host.
host.intfs.append('h_' + self.topo.name(dpid) + '-eth0')
self.nodes[dpid] = host
#lg.info('%s ' % host.name)
def _add_switch(self, dpid):
'''
@param dpid DPID of switch to add
'''
sw = None
if self.switch_is_kernel:
sw = self.switch('s_' + self.topo.name(dpid), 'nl:' + str(self.dps))
self.dps += 1
else:
sw = self.switch('s_' + self.topo.name(dpid))
self.nodes[dpid] = sw
def _add_link(self, src, dst):
'''Add link.
@param src source DPID
@param dst destination DPID
'''
src_port, dst_port = self.topo.port(src, dst)
src_node = self.nodes[src]
dst_node = self.nodes[dst]
src_intf = src_node.intfName(src_port)
dst_intf = dst_node.intfName(dst_port)
make_veth_pair(src_intf, dst_intf)
src_node.intfs.append(src_intf)
dst_node.intfs.append(dst_intf)
#lg.info('\n')
#lg.info('added intf %s to src node %x\n' % (src_intf, src))
#lg.info('added intf %s to dst node %x\n' % (dst_intf, dst))
if src_node.inNamespace:
#lg.info('moving src w/inNamespace set\n')
retry(3, MOVEINTF_DELAY, move_intf, src_intf, src_node)
if dst_node.inNamespace:
#lg.info('moving dst w/inNamespace set\n')
retry(3, MOVEINTF_DELAY, move_intf, dst_intf, dst_node)
src_node.connection[src_intf] = (dst_node, dst_intf)
dst_node.connection[dst_intf] = (src_node, src_intf)
def _add_controller(self, controller):
'''Add controller.
@param controller Controller class
'''
controller = self.controller('c0', kernel = self.kernel)
self.controllers['c0'] = controller
# Control network support:
#
# Create an explicit control network. Currently this is only
# used by the user datapath configuration.
#
# Notes:
#
# 1. If the controller and switches are in the same (e.g. root)
# namespace, they can just use the loopback connection.
# We may wish to do this for the user datapath as well as the
# kernel datapath.
#
# 2. If we can get unix domain sockets to work, we can use them
# instead of an explicit control network.
#
# 3. Instead of routing, we could bridge or use 'in-band' control.
#
# 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!)
def _configureControlNetwork(self):
'''Configure control network.'''
self._configureRoutedControlNetwork()
def _configureRoutedControlNetwork(self):
'''Configure a routed control network on controller and switches.
For use with the user datapath only right now.
@todo(brandonh) Test this code and verify that user-space works!
'''
# params were: controller, switches, ips
controller = self.controllers['c0']
lg.info('%s <-> ' % controller.name)
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
lg.info('%s ' % switch.name)
sip = self.topo.ip(switch_dpid)#ips.next()
sintf = switch.intfs[0]
node, cintf = switch.connection[sintf]
if node != controller:
lg.error('*** Error: switch %s not connected to correct'
'controller' %
switch.name)
exit(1)
controller.setIP(cintf, self.cparams.ip, '/' +
self.cparams.subnet_size)
switch.setIP(sintf, sip, '/' + self.cparams.subnet_size)
controller.setHostRoute(sip, cintf)
switch.setHostRoute(self.cparams.ip, sintf)
lg.info('\n')
lg.info('*** Testing control network\n')
while not controller.intfIsUp(controller.intfs[0]):
lg.info('*** Waiting for %s to come up\n', controller.intfs[0])
sleep(1)
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
while not switch.intfIsUp(switch.intfs[0]):
lg.info('*** Waiting for %s to come up\n' % switch.intfs[0])
sleep(1)
if self.ping_test(hosts=[switch, controller]) != 0:
lg.error('*** Error: control network test failed\n')
exit(1)
lg.info('\n')
def _config_hosts( self ):
'''Configure a set of hosts.'''
# params were: hosts, ips
for host_dpid in self.topo.hosts():
host = self.nodes[host_dpid]
hintf = host.intfs[0]
host.setIP(hintf, self.topo.ip(host_dpid),
'/' + str(self.cparams.subnet_size))
host.setDefaultRoute(hintf)
# You're low priority, dude!
quietRun('renice +18 -p ' + repr(host.pid))
lg.info('%s ', host.name)
lg.info('\n')
def build(self, xterms, cleanup):
'''Build mininet.
At the end of this function, everything should be connected and up.
@param xterms spawn xterms on build?
@param cleanup cleanup before creating?
'''
if cleanup:
pass # cleanup
# validate topo?
kernel = self.kernel
if kernel:
lg.info('*** Using kernel datapath\n')
else:
lg.info('*** Using user datapath\n')
lg.info('*** Adding controller\n')
self._add_controller(self.controller)
lg.info('*** Creating network\n')
lg.info('*** Adding hosts:\n')
for host in sorted(self.topo.hosts()):
self._add_host(host)
lg.info('0x%x ' % host)
lg.info('\n*** Adding switches:\n')
for switch in sorted(self.topo.switches()):
self._add_switch(switch)
lg.info('0x%x ' % switch)
lg.info('\n*** Adding edges: ')
for src, dst in sorted(self.topo.edges()):
self._add_link(src, dst)
lg.info('(0x%x, 0x%x) ' % (src, dst))
lg.info('\n')
if not kernel:
lg.info('*** Configuring control network\n')
self._configureControlNetwork()
lg.info('*** Configuring hosts\n')
self._config_hosts()
if xterms:
pass # build xterms
def start(self):
'''Start controller and switches\n'''
lg.info('*** Starting controller\n')
self.controllers['c0'].start()
#for controller in self.controllers:
# controller.start()
lg.info('*** Starting %s switches\n' % len(self.topo.switches()))
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
#lg.info('switch = %s' % switch)
lg.info('0x%x ' % switch_dpid)
switch.start(self.controllers['c0'])
lg.info('\n')
def stop(self):
'''Stop the controller(s), switches and hosts\n'''
lg.info('*** Stopping %i hosts\n' % len(self.topo.hosts()))
for host_dpid in self.topo.hosts():
host = self.nodes[host_dpid]
lg.info('%s ' % host.name)
host.terminate()
lg.info('\n')
lg.info('*** Stopping %i switches\n' % len(self.topo.switches()))
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
lg.info('%s' % switch.name)
switch.stop()
lg.info('\n')
lg.info('*** Stopping controller\n')
#for controller in self.controllers.iteriterms():
self.controllers['c0'].stop()
lg.info('*** Test complete\n')
def run(self, test, **params):
'''Perform a complete start/test/stop cycle.'''
self.start()
lg.info('*** Running test\n')
result = test(self, **params)
self.stop()
return result
@staticmethod
def _parse_ping(pingOutput):
'''Parse ping output and return packets sent, received.'''
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m == None:
lg.error('*** Error: could not parse ping output: %s\n' %
pingOutput)
exit(1)
sent, received = int(m.group(1)), int(m.group(2))
return sent, received
def ping_test(self, hosts = None, verbose = False):
'''Ping between all specified hosts.
@param hosts list of host DPIDs
@param verbose verbose printing
@return ploss packet loss percentage
'''
#self.start()
# check if running - only then, start?
packets = 0
lost = 0
if not hosts:
hosts = self.topo.hosts()
for node_dpid in hosts:
node = self.nodes[node_dpid]
if verbose:
lg.info('%s -> ' % node.name)
for dest_dpid in hosts:
dest = self.nodes[dest_dpid]
if node != dest:
result = node.cmd('ping -c1 ' + dest.IP())
sent, received = self._parse_ping(result)
packets += sent
if received > sent:
lg.error('*** Error: received too many packets')
lg.error('%s' % result)
node.cmdPrint('route')
exit( 1 )
lost += sent - received
if verbose:
lg.info(('%s ' % dest.name) if received else 'X ')
if verbose:
lg.info('\n')
ploss = 100 * lost/packets
if verbose:
lg.info('%d%% packet loss (%d/%d lost)\n' % (ploss, lost, packets))
#flush()
#self.stop()
return ploss
def interact(self):
'''Start network and run our simple CLI.'''
self.run(Cli)
class Cli(object):
'''Simple command-line interface to talk to nodes.'''
cmds = ['?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \
'ping_pair'] #'iperf'
def __init__(self, mininet):
self.mn = mininet
self.nodemap = {} # map names to Node objects
for node in self.mn.nodes.values():
self.nodemap[node.name] = node
self.nodemap['c0'] = self.mn.controllers['c0']
self.nodelist = self.nodemap.values()
self.run()
# Commands
def help(self, args):
'''Semi-useful help for CLI.'''
help_str = 'Available commands are:' + str(self.cmds) + '\n' + \
'You may also send a command to a node using:\n' + \
' <node> command {args}\n' + \
'For example:\n' + \
' mininet> h0 ifconfig\n' + \
'\n' + \
'The interpreter automatically substitutes IP ' + \
'addresses\n' + \
'for node names, so commands like\n' + \
' mininet> h0 ping -c1 h1\n' + \
'should work.\n' + \
'\n\n' + \
'Interactive commands are not really supported yet,\n' + \
'so please limit commands to ones that do not\n' + \
'require user interaction and will terminate\n' + \
'after a reasonable amount of time.\n'
print(help_str)
def nodes(self, args):
'''List all nodes.'''
lg.info('available nodes are: \n%s\n',
' '.join([node.name for node in sorted(self.nodelist)]))
def net(self, args):
'''List network connection.'''
for switch_dpid in self.mn.topo.switches():
switch = self.mn.nodes[switch_dpid]
lg.info('%s <->', switch.name)
for intf in switch.intfs:
node, remoteIntf = switch.connection[intf]
lg.info(' %s' % node.name)
lg.info('\n')
def sh(self, args):
'''Run an external shell command'''
call( [ 'sh', '-c' ] + args )
def ping_all(self, args):
'''Ping between all hosts.'''
self.mn.ping_test(verbose = True)
def ping_pair(self, args):
'''Ping between first two hosts, useful for testing.'''
hosts_unsorted = sorted(self.mn.topo.hosts())
hosts = [hosts_unsorted[0], hosts_unsorted[1]]
self.mn.ping_test(hosts = hosts, verbose = True)
def run(self):
'''Read and execute commands.'''
lg.warn('*** Starting CLI:\n')
while True:
lg.warn('mininet> ')
input = sys.stdin.readline()
if input == '':
break
if input[-1] == '\n':
input = input[:-1]
cmd = input.split(' ')
first = cmd[0]
rest = cmd[1:]
if first in self.cmds and hasattr(self, first):
getattr(self, first)(rest)
elif first in self.nodemap and rest != []:
node = self.nodemap[first]
# Substitute IP addresses for node names in command
rest = [self.nodemap[arg].IP() if arg in self.nodemap else arg
for arg in rest]
rest = ' '.join(rest)
# Interactive commands don't work yet, and
# there are still issues with control-c
lg.warn('*** %s: running %s\n' % (node.name, rest))
node.sendCmd( rest )
while True:
try:
done, data = node.monitor()
lg.info('%s\n' % data)
if done:
break
except KeyboardInterrupt:
node.sendInt()
elif first == '':
pass
elif first in ['exit', 'quit']:
break
elif first == '?':
self.help( rest )
else:
lg.error('CLI: unknown node or command: < %s >\n' % first)
#lg.info('*** CLI: command complete\n')
return 'exited by user command'
class NOXController(Controller):
'''Controller to run a NOX application.'''
def __init__(self, name, nox_args = None, **kwargs):
'''Init.
@param name name to give controller
@param nox_args list of args to use with NOX
'''
if not nox_args:
nox_args = ['packetdump']
nox_core_dir = os.environ['NOX_CORE_DIR']
if not nox_core_dir:
raise Exception('please set NOX_CORE_DIR env var\n')
Controller.__init__(self, name,
controller = nox_core_dir + '/nox_core',
cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join(nox_args),
cdir = nox_core_dir, **kwargs)
class ControllerParams(object):
'''Container for controller IP parameters.'''
def __init__(self, ip, subnet_size):
'''Init.
@param ip integer, controller IP
@param subnet_size integer, ex 8 for slash-8, covering 17M
'''
self.ip = ip
self.subnet_size = subnet_size
from mininet.logging_mod import set_loglevel
from mininet.net import init, Mininet
from mininet.node import Switch, Host, NOXController, ControllerParams
if __name__ == '__main__':
set_loglevel('info')
init()
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
mn = Mininet(FatTreeTopo(4), Switch, Host, NOXController,
controller_params)
controller_params)
mn.interact()
+466 -486
View File
@@ -66,499 +66,479 @@ History:
12/12/09 Added subdivided network driver workflow
12/13/09 Added support for custom controller and switch classes
"""
from subprocess import call, Popen, PIPE, STDOUT
import os
import re
from subprocess import call
import sys
from time import sleep
import os, re, signal, sys, select
flush = sys.stdout.flush
#flush = sys.stdout.flush
#import os, re, signal, sys, select
from mininet.logging_mod import lg, set_loglevel
from mininet.node import Node, Host, Controller, Switch
from mininet.util import run, checkRun, quietRun, makeIntfPair, moveIntf
from mininet.util import createLink, setLimits
from mininet.node import Host, Controller, Switch, ControllerParams
from mininet.topo import TreeTopo
from mininet.util import quietRun, fixLimits
from mininet.util import make_veth_pair, move_intf, retry, MOVEINTF_DELAY
DATAPATHS = ['user', 'kernel']
# Handy utilities
def dumpNodes( nodes ):
"Dump ifconfig of each node."
for node in nodes:
lg.info("*** Dumping node %s\n" % node.name)
lg.info("%s\n" % node.cmd( 'ip link show' ))
lg.info("%s\n" % node.cmd( 'route' ))
def ipGen( A, B, c, d ):
"Generate next IP class B IP address, starting at A.B.c.d"
while True:
yield '%d.%d.%d.%d' % ( A, B, c, d )
d += 1
if d > 254:
d = 1
c += 1
if c > 254: break
def nameGen( prefix ):
"Generate names starting with prefix."
i = 0
while True: yield prefix + `i`; i += 1
# Control network support:
#
# Create an explicit control network. Currently this is only
# used by the user datapath configuration.
#
# Notes:
#
# 1. If the controller and switches are in the same (e.g. root)
# namespace, they can just use the loopback connection.
# We may wish to do this for the user datapath as well as the
# kernel datapath.
#
# 2. If we can get unix domain sockets to work, we can use them
# instead of an explicit control network.
#
# 3. Instead of routing, we could bridge or use "in-band" control.
#
# 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!)
def configureRoutedControlNetwork( controller, switches, ips):
"""Configure a routed control network on controller and switches,
for use with the user datapath."""
cip = ips.next()
lg.info("%s <-> " % controller.name)
for switch in switches:
lg.info("%s " % switch.name)
sip = ips.next()
sintf = switch.intfs[ 0 ]
node, cintf = switch.connection[ sintf ]
if node != controller:
lg.error("*** Error: switch %s not connected to correct controller" %
switch.name)
exit( 1 )
controller.setIP( cintf, cip, '/24' )
switch.setIP( sintf, sip, '/24' )
controller.setHostRoute( sip, cintf )
switch.setHostRoute( cip, sintf )
lg.info("\n")
lg.info("*** Testing control network\n")
while not controller.intfIsUp():
lg.info("*** Waiting for %s to come up\n", controller.intfs[ 0 ])
sleep( 1 )
for switch in switches:
while not switch.intfIsUp():
lg.info("*** Waiting for %s to come up\n" % switch.intfs[ 0 ])
sleep( 1 )
if pingTest( hosts=[ switch, controller ] ) != 0:
lg.error("*** Error: control network test failed\n")
exit( 1 )
def configHosts( hosts, ips ):
"""Configure a set of hosts, starting at IP address a.b.c.d"""
for host in hosts:
hintf = host.intfs[ 0 ]
host.setIP( hintf, ips.next(), '/24' )
host.setDefaultRoute( hintf )
# You're low priority, dude!
quietRun( 'renice +18 -p ' + `host.pid` )
lg.info("%s ", host.name)
lg.info("\n")
# Test driver and topologies
class Network( object ):
"""Network topology (and test driver) base class."""
def __init__( self,
kernel=True,
Controller=Controller, Switch=Switch,
hostIpGen=ipGen, hostIpStart=( 192, 168, 123, 1 ) ):
self.kernel = kernel
self.Controller = Controller
self.Switch = Switch
self.hostIps = apply( hostIpGen, hostIpStart )
# Check for kernel modules
modules = quietRun( 'lsmod' )
if not kernel and 'tun' not in modules:
lg.error("*** Error: kernel module tun not loaded:\n")
lg.error(" user datapath not supported\n")
exit( 1 )
if kernel and 'ofdatapath' not in modules:
lg.error("*** Error: kernel module ofdatapath not loaded:\n")
lg.error(" kernel datapath not supported\n")
exit( 1 )
# Create network, but don't start things up yet!
self.prepareNet()
def configureControlNetwork( self,
ipGen=ipGen, ipStart = (10, 0, 123, 1 ) ):
ips = apply( ipGen, ipStart )
configureRoutedControlNetwork( self.controllers[ 0 ],
self.switches, ips = ips)
def configHosts( self ):
configHosts( self.hosts, self.hostIps )
def prepareNet( self ):
"""Create a network by calling makeNet as follows:
(switches, hosts ) = makeNet()
Create a controller here as well."""
kernel = self.kernel
if kernel:
lg.info("*** Using kernel datapath\n")
else:
lg.info("*** Using user datapath\n")
lg.info("*** Creating controller\n")
self.controller = self.Controller( 'c0', kernel=kernel )
self.controllers = [ self.controller ]
lg.info("*** Creating network\n")
self.switches, self.hosts = self.makeNet( self.controller )
lg.info("\n")
if not kernel:
lg.info("*** Configuring control network\n")
self.configureControlNetwork()
lg.info("*** Configuring hosts\n")
self.configHosts()
def start( self ):
"""Start controller and switches\n"""
lg.info("*** Starting controller\n")
for controller in self.controllers:
controller.start()
lg.info("*** Starting %s switches" % len(self.switches))
for switch in self.switches:
switch.start( self.controllers[ 0 ] )
lg.info("\n")
def stop( self ):
"""Stop the controller(s), switches and hosts\n"""
lg.info("*** Stopping hosts\n")
for host in self.hosts:
host.terminate()
lg.info("*** Stopping switches\n")
for switch in self.switches:
lg.info("%s" % switch.name)
switch.stop()
lg.info("\n")
lg.info("*** Stopping controller\n")
for controller in self.controllers:
controller.stop();
lg.info("*** Test complete\n")
def runTest( self, test ):
"""Run a given test, called as test( controllers, switches, hosts)"""
return test( self.controllers, self.switches, self.hosts )
def run( self, test ):
"""Perform a complete start/test/stop cycle; test is of the form
test( controllers, switches, hosts )"""
self.start()
lg.info("*** Running test\n")
result = self.runTest( test )
self.stop()
return result
def interact( self ):
"Create a network and run our simple CLI."
self.run( self, Cli )
def defaultNames( snames=None, hnames=None, dpnames=None ):
"Reinitialize default names from generators, if necessary."
if snames is None: snames = nameGen( 's' )
if hnames is None: hnames = nameGen( 'h' )
if dpnames is None: dpnames = nameGen( 'nl:' )
return snames, hnames, dpnames
# Tree network
class TreeNet( Network ):
"A tree-structured network with the specified depth and fanout"
def __init__( self, depth, fanout, **kwargs):
self.depth, self.fanout = depth, fanout
Network.__init__( self, **kwargs )
def treeNet( self, controller, depth, fanout, snames=None,
hnames=None, dpnames=None ):
"""Return a tree network of the given depth and fanout as a triple:
( root, switches, hosts ), using the given switch, host and
datapath name generators, with the switches connected to the given
controller. If kernel=True, use the kernel datapath; otherwise the
user datapath will be used."""
# Ugly, but necessary (?) since defaults are only evaluated once
snames, hnames, dpnames = defaultNames( snames, hnames, dpnames )
if ( depth == 0 ):
host = Host( hnames.next() )
lg.info("%s " % host.name)
return host, [], [ host ]
dp = dpnames.next() if self.kernel else None
switch = Switch( snames.next(), dp )
if not self.kernel: createLink( switch, controller )
lg.info("%s " % switch.name)
switches, hosts = [ switch ], []
for i in range( 0, fanout ):
child, slist, hlist = self.treeNet( controller,
depth - 1, fanout, snames, hnames, dpnames )
createLink( switch, child )
switches += slist
hosts += hlist
return switch, switches, hosts
def makeNet( self, controller ):
root, switches, hosts = self.treeNet( controller,
self.depth, self.fanout )
return switches, hosts
# Grid network
class GridNet( Network ):
"""An N x M grid/mesh network of switches, with hosts at the edges.
This class also demonstrates creating a somewhat complicated
topology."""
def __init__( self, n, m, linear=False, **kwargs ):
self.n, self.m, self.linear = n, m, linear and m == 1
Network.__init__( self, **kwargs )
def makeNet( self, controller ):
snames, hnames, dpnames = defaultNames()
n, m = self.n, self.m
hosts = []
switches = []
kernel = self.kernel
rows = []
if not self.linear:
lg.info("*** gridNet: creating", n, "x", m, "grid of switches")
for y in range( 0, m ):
row = []
for x in range( 0, n ):
dp = dpnames.next() if kernel else None
switch = Switch( snames.next(), dp )
if not kernel: createLink( switch, controller )
row.append( switch )
switches += [ switch ]
lg.info("%s " % switch.name)
rows += [ row ]
# Hook up rows
for row in rows:
previous = None
for switch in row:
if previous is not None:
createLink( switch, previous )
previous = switch
h1, h2 = Host( hnames.next() ), Host( hnames.next() )
createLink( h1, row[ 0 ] )
createLink( h2, row[ -1 ] )
hosts += [ h1, h2 ]
lg.info("%s %s" % (h1.name, h2.name))
# Return here if we're using this to make a linear network
if self.linear: return switches, hosts
# Hook up columns
for x in range( 0, n ):
previous = None
for y in range( 0, m ):
switch = rows[ y ][ x ]
if previous is not None:
createLink( switch, previous )
previous = switch
h1, h2 = Host( hnames.next() ), Host( hnames.next() )
createLink( h1, rows[ 0 ][ x ] )
createLink( h2, rows[ -1 ][ x ] )
hosts += [ h1, h2 ]
lg.info("%s %s" % (h1.name, h2.name))
return switches, hosts
class LinearNet( GridNet ):
"A network consisting of two hosts connected by a string of switches."
def __init__( self, switchCount, **kwargs ):
self.switchCount = switchCount
GridNet.__init__( self, switchCount, 1, linear=True, **kwargs )
# Tests
def parsePing( pingOutput ):
"Parse ping output and return packets sent, received."
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m == None:
lg.error("*** Error: could not parse ping output: %s\n" % pingOutput)
exit( 1 )
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
return sent, received
def pingTest( controllers=[], switches=[], hosts=[], verbose=False ):
"Test that each host can reach every other host."
packets = 0 ; lost = 0
for node in hosts:
if verbose:
lg.info("%s -> " % node.name)
for dest in hosts:
if node != dest:
result = node.cmd( 'ping -c1 ' + dest.IP() )
sent, received = parsePing( result )
packets += sent
if received > sent:
lg.error("*** Error: received too many packets")
lg.error("%s" % result)
node.cmdPrint( 'route' )
exit( 1 )
lost += sent - received
if verbose:
lg.info(("%s " % dest.name) if received else "X ")
if verbose:
lg.info("\n")
ploss = 100 * lost/packets
if verbose:
lg.info("%d%% packet loss (%d/%d lost)\n" % ( ploss, lost, packets ))
flush()
return ploss
def pingTestVerbose( controllers, switches, hosts ):
return "%d %% packet loss" % \
pingTest( controllers, switches, hosts, verbose=True )
def parseIperf( iperfOutput ):
"Parse iperf output and return bandwidth."
r = r'([\d\.]+ \w+/sec)'
m = re.search( r, iperfOutput )
return m.group( 1 ) if m is not None else "could not parse iperf output"
def iperf( hosts, verbose=False ):
"Run iperf between two hosts."
assert len( hosts ) == 2
host1, host2 = hosts[ 0 ], hosts[ 1 ]
host1.cmd( 'killall -9 iperf') # XXX shouldn't be global killall
server = host1.cmd( 'iperf -s &' )
if verbose:
lg.info("%s" % server)
client = host2.cmd( 'iperf -t 5 -c ' + host1.IP() )
if verbose:
lg.info("%s" % client)
server = host1.cmd( 'kill -9 %iperf' )
if verbose:
lg.info("%s" % server)
return [ parseIperf( server ), parseIperf( client ) ]
def iperfTest( controllers, switches, hosts, verbose=False ):
"Simple iperf test between two hosts."
if verbose:
lg.info("*** Starting ping test\n")
h0, hN = hosts[ 0 ], hosts[ -1 ]
lg.info("*** iperfTest: Testing bandwidth between")
lg.info("%s and %s\n" % (h0.name, hN.name))
result = iperf( [ h0, hN], verbose )
lg.info("*** result: %s\n" % result)
return result
# Simple CLI
class Cli( object ):
"Simple command-line interface to talk to nodes."
cmds = [ '?', 'help', 'nodes', 'sh', 'pingtest', 'iperf', 'net', 'exit' ]
def __init__( self, controllers, switches, hosts ):
self.controllers = controllers
self.switches = switches
self.hosts = hosts
self.nodemap = {}
self.nodelist = controllers + switches + hosts
for node in self.nodelist:
self.nodemap[ node.name ] = node
self.run()
# Commands
def help( self, args ):
"Semi-useful help for CLI"
help_str = "Available commands are:" + str(self.cmds) + "\n" + \
"You may also send a command to a node using:" + \
" <node> command {args}" + \
"For example:" + \
" mininet> h0 ifconfig" + \
"\n" + \
"The interpreter automatically substitutes IP addresses" + \
"for node names, so commands like" + \
" mininet> h0 ping -c1 h1" + \
"should work." + \
"\n" + \
"Interactive commands are not really supported yet," + \
"so please limit commands to ones that do not" + \
"require user interaction and will terminate" + \
"after a reasonable amount of time."
print(help_str)
def nodes( self, args ):
"List available nodes"
lg.info("available nodes are:\n", [ node.name for node in self.nodelist])
def sh( self, args ):
"Run an external shell command"
call( [ 'sh', '-c' ] + args )
def pingtest( self, args ):
pingTest( self.controllers, self.switches, self.hosts, verbose=True )
def net( self, args ):
for switch in self.switches:
lg.info("%s <-> ", switch.name)
for intf in switch.intfs:
node, remoteIntf = switch.connection[ intf ]
lg.info("%s" % node.name)
def iperf( self, args ):
if len( args ) != 2:
lg.error("usage: iperf <h1> <h2>")
return
for host in args:
if host not in self.nodemap:
lg.error("iperf: cannot find host: %s" % host)
return
iperf( [ self.nodemap[ h ] for h in args ], verbose=True )
# Interpreter
def run( self ):
"Read and execute commands."
lg.info("*** cli: starting\n")
while True:
lg.info("mininet> ")
input = sys.stdin.readline()
if input == '': break
if input[ -1 ] == '\n': input = input[ : -1 ]
cmd = input.split( ' ' )
first = cmd[ 0 ]
rest = cmd[ 1: ]
if first in self.cmds and hasattr( self, first ):
getattr( self, first )( rest )
elif first in self.nodemap and rest != []:
node = self.nodemap[ first ]
# Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP() if arg in self.nodemap else arg
for arg in rest ]
rest = ' '.join( rest )
# Interactive commands don't work yet, and
# there are still issues with control-c
lg.error("*** %s: running %s\n" % (node.name, rest))
node.sendCmd( rest )
while True:
try:
done, data = node.monitor()
lg.info("%s\n" % data)
if done: break
except KeyboardInterrupt: node.sendInt()
elif first == '': pass
elif first in [ 'exit', 'quit' ]: break
elif first == '?': self.help( rest )
else:
lg.error("cli: unknown node or command: < %s >\n" % first)
lg.info("*** cli: exiting\n")
DATAPATHS = ['kernel'] #['user', 'kernel']
def init():
"Initialize Mininet."
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
print "*** Mininet must run as root."; exit( 1 )
# If which produces no output, then netns is not in the path.
# May want to loosen this to handle netns in the current dir.
if not quietRun( [ 'which', 'netns' ] ):
raise Exception( "Could not find netns; see INSTALL" )
fixLimits()
"Initialize Mininet."
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
print "*** Mininet must run as root."
exit(1)
# If which produces no output, then netns is not in the path.
# May want to loosen this to handle netns in the current dir.
if not quietRun(['which', 'netns']):
raise Exception("Could not find netns; see INSTALL")
fixLimits()
class Mininet(object):
'''Network emulation with hosts spawned in network namespaces.'''
def __init__(self, topo, switch, host, controller, cparams,
build = True, xterms = False, cleanup = False,
in_namespace = False, switch_is_kernel = True):
'''Create Mininet object.
@param topo Topo object
@param switch Switch class
@param host Host class
@param controller Controller class
@param cparams ControllerParams object
@param now build now?
@param xterms if build now, spawn xterms?
@param cleanup if build now, cleanup before creating?
@param in_namespace spawn switches and hosts in their own namespace?
'''
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.in_namespace = in_namespace
self.switch_is_kernel = switch_is_kernel
self.kernel = True #temporary!
if build:
self.build(xterms, cleanup)
def _add_host(self, dpid):
'''Add host.
@param dpid DPID of host to add
'''
host = self.host('h_' + self.topo.name(dpid))
# for now, assume one interface per host.
host.intfs.append('h_' + self.topo.name(dpid) + '-eth0')
self.nodes[dpid] = host
#lg.info('%s ' % host.name)
def _add_switch(self, dpid):
'''
@param dpid DPID of switch to add
'''
sw = None
if self.switch_is_kernel:
sw = self.switch('s_' + self.topo.name(dpid), 'nl:' + str(self.dps))
self.dps += 1
else:
sw = self.switch('s_' + self.topo.name(dpid))
self.nodes[dpid] = sw
def _add_link(self, src, dst):
'''Add link.
@param src source DPID
@param dst destination DPID
'''
src_port, dst_port = self.topo.port(src, dst)
src_node = self.nodes[src]
dst_node = self.nodes[dst]
src_intf = src_node.intfName(src_port)
dst_intf = dst_node.intfName(dst_port)
make_veth_pair(src_intf, dst_intf)
src_node.intfs.append(src_intf)
dst_node.intfs.append(dst_intf)
#lg.info('\n')
#lg.info('added intf %s to src node %x\n' % (src_intf, src))
#lg.info('added intf %s to dst node %x\n' % (dst_intf, dst))
if src_node.inNamespace:
#lg.info('moving src w/inNamespace set\n')
retry(3, MOVEINTF_DELAY, move_intf, src_intf, src_node)
if dst_node.inNamespace:
#lg.info('moving dst w/inNamespace set\n')
retry(3, MOVEINTF_DELAY, move_intf, dst_intf, dst_node)
src_node.connection[src_intf] = (dst_node, dst_intf)
dst_node.connection[dst_intf] = (src_node, src_intf)
def _add_controller(self, controller):
'''Add controller.
@param controller Controller class
'''
controller = self.controller('c0', kernel = self.kernel)
self.controllers['c0'] = controller
# Control network support:
#
# Create an explicit control network. Currently this is only
# used by the user datapath configuration.
#
# Notes:
#
# 1. If the controller and switches are in the same (e.g. root)
# namespace, they can just use the loopback connection.
# We may wish to do this for the user datapath as well as the
# kernel datapath.
#
# 2. If we can get unix domain sockets to work, we can use them
# instead of an explicit control network.
#
# 3. Instead of routing, we could bridge or use 'in-band' control.
#
# 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!)
def _configureControlNetwork(self):
'''Configure control network.'''
self._configureRoutedControlNetwork()
def _configureRoutedControlNetwork(self):
'''Configure a routed control network on controller and switches.
For use with the user datapath only right now.
@todo(brandonh) Test this code and verify that user-space works!
'''
# params were: controller, switches, ips
controller = self.controllers['c0']
lg.info('%s <-> ' % controller.name)
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
lg.info('%s ' % switch.name)
sip = self.topo.ip(switch_dpid)#ips.next()
sintf = switch.intfs[0]
node, cintf = switch.connection[sintf]
if node != controller:
lg.error('*** Error: switch %s not connected to correct'
'controller' %
switch.name)
exit(1)
controller.setIP(cintf, self.cparams.ip, '/' +
self.cparams.subnet_size)
switch.setIP(sintf, sip, '/' + self.cparams.subnet_size)
controller.setHostRoute(sip, cintf)
switch.setHostRoute(self.cparams.ip, sintf)
lg.info('\n')
lg.info('*** Testing control network\n')
while not controller.intfIsUp(controller.intfs[0]):
lg.info('*** Waiting for %s to come up\n', controller.intfs[0])
sleep(1)
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
while not switch.intfIsUp(switch.intfs[0]):
lg.info('*** Waiting for %s to come up\n' % switch.intfs[0])
sleep(1)
if self.ping_test(hosts=[switch, controller]) != 0:
lg.error('*** Error: control network test failed\n')
exit(1)
lg.info('\n')
def _config_hosts( self ):
'''Configure a set of hosts.'''
# params were: hosts, ips
for host_dpid in self.topo.hosts():
host = self.nodes[host_dpid]
hintf = host.intfs[0]
host.setIP(hintf, self.topo.ip(host_dpid),
'/' + str(self.cparams.subnet_size))
host.setDefaultRoute(hintf)
# You're low priority, dude!
quietRun('renice +18 -p ' + repr(host.pid))
lg.info('%s ', host.name)
lg.info('\n')
def build(self, xterms, cleanup):
'''Build mininet.
At the end of this function, everything should be connected and up.
@param xterms spawn xterms on build?
@param cleanup cleanup before creating?
'''
if cleanup:
pass # cleanup
# validate topo?
kernel = self.kernel
if kernel:
lg.info('*** Using kernel datapath\n')
else:
lg.info('*** Using user datapath\n')
lg.info('*** Adding controller\n')
self._add_controller(self.controller)
lg.info('*** Creating network\n')
lg.info('*** Adding hosts:\n')
for host in sorted(self.topo.hosts()):
self._add_host(host)
lg.info('0x%x ' % host)
lg.info('\n*** Adding switches:\n')
for switch in sorted(self.topo.switches()):
self._add_switch(switch)
lg.info('0x%x ' % switch)
lg.info('\n*** Adding edges: ')
for src, dst in sorted(self.topo.edges()):
self._add_link(src, dst)
lg.info('(0x%x, 0x%x) ' % (src, dst))
lg.info('\n')
if not kernel:
lg.info('*** Configuring control network\n')
self._configureControlNetwork()
lg.info('*** Configuring hosts\n')
self._config_hosts()
if xterms:
pass # build xterms
def start(self):
'''Start controller and switches\n'''
lg.info('*** Starting controller\n')
self.controllers['c0'].start()
#for controller in self.controllers:
# controller.start()
lg.info('*** Starting %s switches\n' % len(self.topo.switches()))
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
#lg.info('switch = %s' % switch)
lg.info('0x%x ' % switch_dpid)
switch.start(self.controllers['c0'])
lg.info('\n')
def stop(self):
'''Stop the controller(s), switches and hosts\n'''
lg.info('*** Stopping %i hosts\n' % len(self.topo.hosts()))
for host_dpid in self.topo.hosts():
host = self.nodes[host_dpid]
lg.info('%s ' % host.name)
host.terminate()
lg.info('\n')
lg.info('*** Stopping %i switches\n' % len(self.topo.switches()))
for switch_dpid in self.topo.switches():
switch = self.nodes[switch_dpid]
lg.info('%s' % switch.name)
switch.stop()
lg.info('\n')
lg.info('*** Stopping controller\n')
#for controller in self.controllers.iteriterms():
self.controllers['c0'].stop()
lg.info('*** Test complete\n')
def run(self, test, **params):
'''Perform a complete start/test/stop cycle.'''
self.start()
lg.info('*** Running test\n')
result = test(self, **params)
self.stop()
return result
@staticmethod
def _parse_ping(pingOutput):
'''Parse ping output and return packets sent, received.'''
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m == None:
lg.error('*** Error: could not parse ping output: %s\n' %
pingOutput)
exit(1)
sent, received = int(m.group(1)), int(m.group(2))
return sent, received
def ping_test(self, hosts = None, verbose = False):
'''Ping between all specified hosts.
@param hosts list of host DPIDs
@param verbose verbose printing
@return ploss packet loss percentage
'''
#self.start()
# check if running - only then, start?
packets = 0
lost = 0
ploss = None
if not hosts:
hosts = self.topo.hosts()
for node_dpid in hosts:
node = self.nodes[node_dpid]
if verbose:
lg.info('%s -> ' % node.name)
for dest_dpid in hosts:
dest = self.nodes[dest_dpid]
if node != dest:
result = node.cmd('ping -c1 ' + dest.IP())
sent, received = self._parse_ping(result)
packets += sent
if received > sent:
lg.error('*** Error: received too many packets')
lg.error('%s' % result)
node.cmdPrint('route')
exit( 1 )
lost += sent - received
if verbose:
lg.info(('%s ' % dest.name) if received else 'X ')
if verbose:
lg.info('\n')
ploss = 100 * lost / packets
if verbose:
lg.info('%d%% packet loss (%d/%d lost)\n' % (ploss, lost, packets))
#flush()
#self.stop()
return ploss
def interact(self):
'''Start network and run our simple CLI.'''
self.run(MininetCLI)
class MininetCLI(object):
'''Simple command-line interface to talk to nodes.'''
cmds = ['?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \
'ping_pair'] #'iperf'
def __init__(self, mininet):
self.mn = mininet
self.nodemap = {} # map names to Node objects
for node in self.mn.nodes.values():
self.nodemap[node.name] = node
self.nodemap['c0'] = self.mn.controllers['c0']
self.nodelist = self.nodemap.values()
self.run()
# Commands
def help(self, args):
'''Semi-useful help for CLI.'''
help_str = 'Available commands are:' + str(self.cmds) + '\n' + \
'You may also send a command to a node using:\n' + \
' <node> command {args}\n' + \
'For example:\n' + \
' mininet> h0 ifconfig\n' + \
'\n' + \
'The interpreter automatically substitutes IP ' + \
'addresses\n' + \
'for node names, so commands like\n' + \
' mininet> h0 ping -c1 h1\n' + \
'should work.\n' + \
'\n\n' + \
'Interactive commands are not really supported yet,\n' + \
'so please limit commands to ones that do not\n' + \
'require user interaction and will terminate\n' + \
'after a reasonable amount of time.\n'
print(help_str)
def nodes(self, args):
'''List all nodes.'''
lg.info('available nodes are: \n%s\n',
' '.join([node.name for node in sorted(self.nodelist)]))
def net(self, args):
'''List network connection.'''
for switch_dpid in self.mn.topo.switches():
switch = self.mn.nodes[switch_dpid]
lg.info('%s <->', switch.name)
for intf in switch.intfs:
node, remoteIntf = switch.connection[intf]
lg.info(' %s' % node.name)
lg.info('\n')
def sh(self, args):
'''Run an external shell command'''
call( [ 'sh', '-c' ] + args )
def ping_all(self, args):
'''Ping between all hosts.'''
self.mn.ping_test(verbose = True)
def ping_pair(self, args):
'''Ping between first two hosts, useful for testing.'''
hosts_unsorted = sorted(self.mn.topo.hosts())
hosts = [hosts_unsorted[0], hosts_unsorted[1]]
self.mn.ping_test(hosts = hosts, verbose = True)
def run(self):
'''Read and execute commands.'''
lg.warn('*** Starting CLI:\n')
while True:
lg.warn('mininet> ')
input = sys.stdin.readline()
if input == '':
break
if input[-1] == '\n':
input = input[:-1]
cmd = input.split(' ')
first = cmd[0]
rest = cmd[1:]
if first in self.cmds and hasattr(self, first):
getattr(self, first)(rest)
elif first in self.nodemap and rest != []:
node = self.nodemap[first]
# Substitute IP addresses for node names in command
rest = [self.nodemap[arg].IP() if arg in self.nodemap else arg
for arg in rest]
rest = ' '.join(rest)
# Interactive commands don't work yet, and
# there are still issues with control-c
lg.warn('*** %s: running %s\n' % (node.name, rest))
node.sendCmd(rest)
while True:
try:
done, data = node.monitor()
lg.info('%s\n' % data)
if done:
break
except KeyboardInterrupt:
node.sendInt()
elif first == '':
pass
elif first in ['exit', 'quit']:
break
elif first == '?':
self.help( rest )
else:
lg.error('CLI: unknown node or command: < %s >\n' % first)
#lg.info('*** CLI: command complete\n')
return 'exited by user command'
if __name__ == '__main__':
if len(sys.argv) > 1:
set_loglevel(sys.argv[1])
else:
set_loglevel('info')
if len(sys.argv) > 1:
set_loglevel(sys.argv[1])
else:
set_loglevel('info')
init()
results = {}
lg.info("*** Welcome to Mininet!\n")
lg.info("*** Look in examples/ for more examples\n\n")
lg.info("*** Testing Mininet with kernel and user datapath\n")
for datapath in [ 'kernel', 'user' ]:
k = datapath == 'kernel'
network = TreeNet( depth=2, fanout=4, kernel=k)
result = network.run( pingTestVerbose )
results[ datapath ] = result
lg.info("*** Test results: %s\n", results)
init()
results = {}
lg.info("*** Welcome to Mininet!\n")
lg.info("*** Look in examples/ for more examples\n\n")
lg.info("*** Testing Mininet with kernel and user datapath\n")
for datapath in DATAPATHS:
k = datapath == 'kernel'
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
mn = Mininet(TreeTopo(), Switch, Host, Controller,
controller_params)
mn.start()
dropped = mn.ping_test()
results[datapath] = "%i%% dropped" % dropped
mn.stop()
lg.info("*** Test results: %s\n", results)
+33 -1
View File
@@ -335,4 +335,36 @@ class Switch(Node):
if not self.execed:
return Node.monitor(self)
else:
return True, ''
return True, ''
class NOXController(Controller):
'''Controller to run a NOX application.'''
def __init__(self, name, nox_args = None, **kwargs):
'''Init.
@param name name to give controller
@param nox_args list of args to use with NOX
'''
if not nox_args:
nox_args = ['packetdump']
nox_core_dir = os.environ['NOX_CORE_DIR']
if not nox_core_dir:
raise Exception('please set NOX_CORE_DIR env var\n')
Controller.__init__(self, name,
controller = nox_core_dir + '/nox_core',
cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join(nox_args),
cdir = nox_core_dir, **kwargs)
class ControllerParams(object):
'''Container for controller IP parameters.'''
def __init__(self, ip, subnet_size):
'''Init.
@param ip integer, controller IP
@param subnet_size integer, ex 8 for slash-8, covering 17M
'''
self.ip = ip
self.subnet_size = subnet_size
+31 -17
View File
@@ -7,7 +7,13 @@ Test creation and all-pairs ping for each included mininet topo type.
from time import sleep
import unittest
from mininet.net import init, TreeNet, LinearNet, pingTest, DATAPATHS
from mininet.net import init, Mininet #, DATAPATHS
from mininet.node import Switch, Host, NOXController, ControllerParams
from mininet.node import Controller
from mininet.topo import TreeTopo
# temporary, until user-space side is tested
DATAPATHS = ['kernel']
class testMinimal(unittest.TestCase):
'''For each datapath type, test ping with a minimal topology.
@@ -20,9 +26,13 @@ class testMinimal(unittest.TestCase):
init()
for datapath in DATAPATHS:
k = datapath == 'kernel'
network = TreeNet(depth = 1, fanout = 2, kernel = k)
dropped = network.run(pingTest)
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
mn = Mininet(TreeTopo(), Switch, Host, Controller,
controller_params)
mn.start()
dropped = mn.ping_test()
self.assertEqual(dropped, 0)
mn.stop()
class testTree(unittest.TestCase):
@@ -33,22 +43,26 @@ class testTree(unittest.TestCase):
init()
for datapath in DATAPATHS:
k = datapath == 'kernel'
network = TreeNet(depth = 2, fanout = 4, kernel = k)
dropped = network.run(pingTest)
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
tree_topo = TreeTopo(depth = 3, fanout = 4)
mn = Mininet(tree_topo, Switch, Host, Controller,
controller_params)
mn.start()
dropped = mn.ping_test()
self.assertEqual(dropped, 0)
mn.stop()
class testLinear(unittest.TestCase):
'''For each datapath type, test all-pairs ping with LinearNet.'''
def testLinear10(self):
'''Ping test with both datapaths on 10-switch topology'''
init()
for datapath in DATAPATHS:
k = datapath == 'kernel'
network = network = LinearNet(10, kernel=k)
dropped = network.run(pingTest)
self.assertEqual(dropped, 0)
#class testLinear(unittest.TestCase):
# '''For each datapath type, test all-pairs ping with LinearNet.'''
#
# def testLinear10(self):
# '''Ping test with both datapaths on 10-switch topology'''
# init()
# for datapath in DATAPATHS:
# k = datapath == 'kernel'
# network = network = LinearNet(10, kernel=k)
# dropped = network.run(pingTest)
# self.assertEqual(dropped, 0)
if __name__ == '__main__':
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python
'''Starter topologies for Mininet.'''
from ripcord.topo import Topo, StructuredNodeSpec, StructuredNode, Edge
from ripcord.topo import StructuredTopo, StructuredEdgeSpec, NodeID
class TreeTopo(StructuredTopo):
'''Tree-structured network.'''
class TreeNodeID(NodeID):
'''Tree-specific node.'''
def __init__(self, layer = 0, index = 0, dpid = None):
'''Create TreeNodeID object from custom params.
Either (layer, index) or dpid must be passed in.
@param layer layer
@param index index within layer
@param dpid optional dpid
'''
if dpid:
self.layer = (dpid & 0xff0000) >> 16
self.index = (dpid & 0xffff)
self.dpid = dpid
else:
self.layer = layer
self.index = index
self.dpid = (layer << 16) + index
def __str__(self):
return "(%i_%i)" % (self.layer, self.index)
def name_str(self):
return "%i_%i" % (self.layer, self.index)
def ip_str(self):
# add 1; can't have IP addr ending in 0
index_hi = (self.index & 0xff00) >> 8
index_lo = self.index & 0xff
return "10.%i.%i.%i" % (self.layer, index_hi, index_lo)
def __init__(self, depth = 2, fanout = 2, speed = 1.0, enable_all = True):
'''Init.
@param depth number of levels, including host level
@param fanout
'''
node_specs = []
core = StructuredNodeSpec(0, fanout, None, speed, type_str = 'core')
node_specs.append(core)
for i in range(1, depth - 1):
node = StructuredNodeSpec(1, fanout, speed, speed,
type_str = 'layer' + str(i))
node_specs.append(node)
host = StructuredNodeSpec(1, 0, speed, None, type_str = 'host')
node_specs.append(host)
edge_specs = [StructuredEdgeSpec(speed)] * (depth - 1)
super(TreeTopo, self).__init__(node_specs, edge_specs)
self.depth = depth
self.fanout = fanout
self.id_gen = TreeTopo.TreeNodeID
# create root
root_id = self.id_gen(0, 0).dpid
self._add_node(root_id, StructuredNode(0))
last_layer_ids = [root_id]
# create lower layers
for i in range(1, depth):
current_layer_ids = []
# start index at 1, as we can't have IP addresses ending in 0
index = 1
for last_id in last_layer_ids:
for j in range(fanout):
is_switch = (i < depth - 1)
node = StructuredNode(i, is_switch = is_switch)
node_id = self.id_gen(i, index).dpid
current_layer_ids.append(node_id)
self._add_node(node_id, node)
self._add_edge(last_id, node_id, Edge())
index += 1
last_layer_ids = current_layer_ids
if enable_all:
self.enable_all()
def port(self, src, dst):
'''Get port number (optional)
Note that the topological significance of DPIDs in FatTreeTopo enables
this function to be implemented statelessly.
@param src source switch DPID
@param dst destination switch DPID
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
src_layer = self.node_info[src].layer
dst_layer = self.node_info[dst].layer
src_id = self.id_gen(dpid = src)
dst_id = self.id_gen(dpid = dst)
lower = None
higher = None
if src_layer == dst_layer - 1: # src is higher
src_port = ((dst_id.index - 1) % self.fanout) + 1
dst_port = 0
elif dst_layer == src_layer - 1:
src_port = 0
dst_port = ((src_id.index - 1) % self.fanout) + 1
else:
raise Exception("Could not find port leading to given dst switch")
return (src_port, dst_port)
def name(self, dpid):
'''Get string name of node ID.
@param dpid DPID of host or switch
@return name_str string name with no dashes
'''
return self.id_gen(dpid = dpid).name_str()
def ip(self, dpid):
'''Get IP dotted-decimal string of node ID.
@param dpid DPID of host or switch
@return ip_str
'''
return self.id_gen(dpid = dpid).ip_str()