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

Intf and Link classes. Latter support bandwidth limits using tc.

parent 6f446f6e
No related branches found
No related tags found
No related merge requests found
"""
link.py: interface and link abstractions for mininet
It seems useful to bundle functionality for interfaces into a single
class.
Also it seems useful to enable the possibility of multiple flavors of
links, including:
- simple veth pairs
- tunneled links
- patchable links (which can be disconnected and reconnected via a patchbay)
- link simulators (e.g. wireless)
Basic division of labor:
Nodes: know how to execute commands
Intfs: know how to configure themselves
Links: know how to connect nodes together
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
from time import sleep
import re
class BasicIntf( object ):
"Basic interface object that can configure itself."
def __init__( self, node, name=None, link=None, **kwargs ):
"""node: owning node (where this intf most likely lives)
name: interface name (e.g. h1-eth0)
link: parent link if any
other arguments are used to configure link parameters"""
self.node = node
self.name = name
self.link = link
self.mac, self.ip = None, None
self.config( **kwargs )
def cmd( self, *args, **kwargs ):
self.node.cmd( *args, **kwargs )
def ifconfig( self, *args ):
"Configure ourselves using ifconfig"
return self.cmd( 'ifconfig', self.name, *args )
def setIP( self, ipstr ):
"""Set our IP address"""
# This is a sign that we should perhaps rethink our prefix
# mechanism
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
def setMAC( self, macstr ):
"""Set the MAC address for an interface.
macstr: MAC address as string"""
self.mac = macstr
return ( self.ifconfig( 'down' ) +
self.ifconfig( 'hw', 'ether', macstr ) +
self.ifconfig( 'up' ) )
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
_macMatchRegex = re.compile( r'..:..:..:..:..:..' )
def updateIP( self ):
"Return updated IP address based on ifconfig"
ifconfig = self.ifconfig()
ips = self._ipMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
return self.ip
def updateMAC( self, intf ):
"Return updated MAC address based on ifconfig"
ifconfig = self.ifconfig()
macs = self._macMatchRegex.findall( ifconfig )
self.mac = macs[ 0 ] if macs else None
return self.mac
def IP( self ):
"Return IP address"
return self.ip
def MAC( self ):
"Return MAC address"
return self.mac
def isUp( self, set=False ):
"Return whether interface is up"
return "UP" in self.ifconfig()
# Map of config params to config methods
# Perhaps this could be more graceful, but it
# is flexible
configMap = { 'mac': 'setMAC',
'ip': 'setIP',
'ifconfig': 'ifconfig' }
def config( self, **params ):
"Configure interface based on parameters"
self.__dict__.update(**params)
for name, value in params.iteritems():
method = self.configMap.get( name, None )
if method:
if type( value ) is str:
value = value.split( ',' )
method( value )
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
# Does it help to sleep to let things run?
sleep( 0.001 )
def __str__( self ):
return self.name
class TCIntf( BasicIntf ):
"Interface customized by tc (traffic control) utility"
def config( self, bw=None, delay=None, loss=0, disable_gro=True,
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
enable_red=False, max_queue_size=1000, **kwargs ):
"Configure the port and set its properties."
BasicIntf.config( self, **kwargs)
# disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
if bw is None and not delay and not loss:
return
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
return
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
return
if loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
return
if delay is None:
delay = '0ms'
if bw is not None and delay is not None:
info( self, '(bw %.2fMbit, delay %s, loss %d%%)\n' %
( bw, delay, loss ) )
# BL: hmm... what exactly is this???
# This seems kind of brittle
if speedup > 0 and self.node.name[0:2] == 'sw':
bw = speedup
tc = 'tc' # was getCmd( 'tc' )
# Bandwidth control algorithms
if use_hfsc:
cmds = [ '%s qdisc del dev %s root',
'%s qdisc add dev %s root handle 1:0 hfsc default 1' ]
if bw is not None:
cmds.append( '%s class add dev %s parent 1:0 classid 1:1 hfsc sc ' +
'rate %fMbit ul rate %fMbit' % ( bw, bw ) )
elif use_tbf:
latency_us = 10 * 1500 * 8 / bw
cmds = ['%s qdisc del dev %s root',
'%s qdisc add dev %s root handle 1: tbf ' +
'rate %fMbit burst 15000 latency %fus' % (bw, latency_us) ]
else:
cmds = [ '%s qdisc del dev %s root',
'%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
# ECN or RED
if enable_ecn:
info( 'Enabling ECN\n' )
cmds += [ '%s qdisc add dev %s parent 1:1 '+
'handle 10: red limit 1000000 '+
'min 20000 max 25000 avpkt 1000 '+
'burst 20 '+
'bandwidth %fmbit probability 1 ecn' % bw ]
elif enable_red:
info( 'Enabling RED\n' )
cmds += [ '%s qdisc add dev %s parent 1:1 '+
'handle 10: red limit 1000000 '+
'min 20000 max 25000 avpkt 1000 '+
'burst 20 '+
'bandwidth %fmbit probability 1' % bw ]
else:
cmds += [ '%s qdisc add dev %s parent 1:1 handle 10:0 netem ' +
'delay ' + '%s' % delay + ' loss ' + '%d' % loss +
' limit %d' % (max_queue_size) ]
# Execute all the commands in the container
debug("at map stage w/cmds: %s\n" % cmds)
def doConfigPort(s):
c = s % (tc, self)
debug(" *** executing command: %s\n" % c)
return self.cmd(c)
outputs = [ doConfigPort(cmd) for cmd in cmds ]
debug( "outputs: %s\n" % outputs )
Intf = TCIntf
class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
def __init__( self, node1, node2, port1=None, port2=None, intfName1=None, intfName2=None,
intf=Intf, params1={}, params2={} ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
port1: node1 port number (optional)
port2: node2 port number (optional)
intfName1: node1 interface name (optional)
intfName2: node2 interface name (optional)"""
# This is a bit awkward; it seems that having everything in
# params would be more orthogonal, but being able to specify
# in-line arguments is more convenient!
if port1 is None:
port1 = node1.newPort()
if port2 is None:
port2 = node2.newPort()
if not intfName1:
intfName1 = self.intfName( node1, port1 )
if not intfName2:
intfName2 = self.intfName( node2, port2 )
self.makeIntfPair( intfName1, intfName2 )
intf1 = intf( name=intfName1, node=node1, link=self, **params1 )
intf2 = intf( name=intfName2, node=node2, link=self, **params2 )
# Add to nodes
node1.addIntf( intf1 )
node2.addIntf( intf2 )
self.intf1, self.intf2 = intf1, intf2
@classmethod
def intfName( cls, node, n ):
"Construct a canonical interface name node-ethN for interface n."
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( cls, intf1, intf2 ):
"""Create pair of interfaces
intf1: name of interface 1
intf2: name of interface 2
(override this class method [and possibly delete()] to change link type)"""
makeIntfPair( intf1, intf2 )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf2.delete()
def __str__( self ):
return '%s<->%s' % ( self.intf1, self.intf2 )
"""
Mininet: A simple networking testbed for OpenFlow!
Mininet: A simple networking testbed for OpenFlow/SDN!
author: Bob Lantz (rlantz@cs.stanford.edu)
author: Brandon Heller (brandonh@stanford.edu)
......@@ -96,6 +96,7 @@
from mininet.log import info, error, debug, output
from mininet.node import Host, UserSwitch, OVSKernelSwitch, Controller
from mininet.node import ControllerParams
from mininet.link import Link
from mininet.util import quietRun, fixLimits
from mininet.util import createLink, macColonHex, ipStr, ipParse
from mininet.term import cleanUpScreens, makeTerms
......@@ -104,16 +105,17 @@ class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=Controller,
controller=Controller, link=Link,
cparams=ControllerParams( '10.0.0.0', 8 ),
build=True, xterms=False, cleanup=False,
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, listenPort=None ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: Switch class
host: Host class
controller: Controller class
switch: default Switch class
host: default Host class/constructor
controller: default Controller class/constructor
link: default Link class/constructor
cparams: ControllerParams object
build: build now from topo?
xterms: if build now, spawn xterms?
......@@ -126,6 +128,7 @@ def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
self.switch = switch
self.host = host
self.controller = controller
self.link = link
self.cparams = cparams
self.topo = topo
self.inNamespace = inNamespace
......@@ -150,30 +153,38 @@ def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
if topo and build:
self.build()
def addHost( self, name, mac=None, ip=None ):
# BL Note:
# The specific items for host/switch/etc. should probably be
# handled in the node classes rather than here!!
def addHost( self, name, mac=None, ip=None, host=None, **params ):
"""Add host.
name: name of host to add
mac: default MAC address for intf 0
ip: default IP address for intf 0
returns: added host"""
host = self.host( name, defaultMAC=mac, defaultIP=ip )
self.hosts.append( host )
self.nameToNode[ name ] = host
return host
def addSwitch( self, name, mac=None, ip=None ):
if not host:
host = self.host
defaults = { 'defaultMAC': mac, 'defaultIP': ip }
defaults.update( params )
h = host( name, **defaults)
self.hosts.append( h )
self.nameToNode[ name ] = h
return h
def addSwitch( self, name, switch=None, **params ):
"""Add switch.
name: name of switch to add
mac: default MAC address for kernel/OVS switch intf 0
returns: added switch
side effect: increments the listenPort member variable."""
if self.switch == UserSwitch:
sw = self.switch( name, listenPort=self.listenPort,
defaultMAC=mac, defaultIP=ip, inNamespace=self.inNamespace )
else:
sw = self.switch( name, listenPort=self.listenPort,
defaultMAC=mac, defaultIP=ip, dp=self.dps,
inNamespace=self.inNamespace )
side effect: increments listenPort and dps ivars."""
defaults = { 'listenPort': self.listenPort,
'inNamespace': self.inNamespace }
if not switch:
switch = self.switch
if switch != UserSwitch:
defaults[ 'dps' ] = self.dps
defaults.update( params )
sw = self.switch( name, **defaults )
if not self.inNamespace and self.listenPort:
self.listenPort += 1
self.dps += 1
......@@ -181,12 +192,12 @@ def addSwitch( self, name, mac=None, ip=None ):
self.nameToNode[ name ] = sw
return sw
def addController( self, name='c0', controller=None, **kwargs ):
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
if not controller:
controller = self.controller
controller_new = controller( name, **kwargs )
controller_new = controller( name, **params )
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
......@@ -210,6 +221,9 @@ def addController( self, name='c0', controller=None, **kwargs ):
# 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!)
#
# 5. Basically nobody ever uses this method, so perhaps it should be moved
# out of this core class.
def configureControlNetwork( self ):
"Configure control network."
......@@ -221,8 +235,7 @@ def configureControlNetwork( self ):
def configureRoutedControlNetwork( self, ip='192.168.123.1',
prefixLen=16 ):
"""Configure a routed control network on controller and switches.
For use with the user datapath only right now.
"""
For use with the user datapath only right now."""
controller = self.controllers[ 0 ]
info( controller.name + ' <->' )
cip = ip
......@@ -256,8 +269,8 @@ def configHosts( self ):
"Configure a set of hosts."
# params were: hosts, ips
for host in self.hosts:
hintf = host.intfs[ 0 ]
host.setIP( hintf, host.defaultIP, self.cparams.prefixLen )
hintf = host.defaultIntf()
host.setIP( host.defaultIP, self.cparams.prefixLen, hintf )
host.setDefaultRoute( hintf )
# You're low priority, dude!
quietRun( 'renice +18 -p ' + repr( host.pid ) )
......@@ -272,9 +285,11 @@ def buildFromTopo( self, topo ):
def addNode( prefix, addMethod, nodeId ):
"Add a host or a switch."
name = prefix + topo.name( nodeId )
# MAC and IP should probably be from nodeInfo...
mac = macColonHex( nodeId ) if self.setMacs else None
ip = topo.ip( nodeId )
node = addMethod( name, mac=mac, ip=ip )
ni = topo.nodeInfo( nodeId )
node = addMethod( name, cls=ni.cls, mac=mac, ip=ip, **ni.params )
self.idToNode[ nodeId ] = node
info( name + ' ' )
......@@ -291,12 +306,16 @@ def addNode( prefix, addMethod, nodeId ):
addNode( 'h', self.addHost, hostId )
info( '\n*** Adding switches:\n' )
for switchId in sorted( topo.switches() ):
addNode( 's', self.addSwitch, switchId )
addNode( 's', self.addSwitch, switchId)
info( '\n*** Adding links:\n' )
for srcId, dstId in sorted( topo.edges() ):
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
srcPort, dstPort = topo.port( srcId, dstId )
createLink( src, dst, srcPort, dstPort )
ei = topo.edgeInfo( srcId, dstId )
link, params = ei.cls, ei.params
if not link:
link = self.link
link( src, dst, srcPort, dstPort, **params )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
......@@ -510,7 +529,7 @@ def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
servout += server.monitor()
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
output('waiting for iperf to start up')
output('waiting for iperf to start up...')
sleep(.5)
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
......
......@@ -46,11 +46,11 @@
import signal
import select
from subprocess import Popen, PIPE, STDOUT
from time import sleep
from mininet.log import info, error, debug
from mininet.util import quietRun, errRun, makeIntfPair, moveIntf, isShellBuiltin
from mininet.util import quietRun, errRun, moveIntf, isShellBuiltin
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
from mininet.link import Link
SWITCH_PORT_BASE = 1 # For OF > 0.9, switch ports start at 1 rather than zero
......@@ -78,7 +78,7 @@ def __init__( self, name, inNamespace=True,
opts += 'n'
cmd = [ 'mnexec', opts, 'bash', '-m' ]
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
close_fds=False )
close_fds=True )
self.stdin = self.shell.stdin
self.stdout = self.shell.stdout
self.pid = self.shell.pid
......@@ -89,12 +89,10 @@ def __init__( self, name, inNamespace=True,
# using select.poll()
self.outToNode[ self.stdout.fileno() ] = self
self.inToNode[ self.stdin.fileno() ] = self
self.intfs = {} # dict of port numbers to interface names
self.ports = {} # dict of interface names to port numbers
self.intfs = {} # dict of port numbers to interfaces
self.ports = {} # dict of interfaces to port numbers
# replace with Port objects, eventually ?
self.ips = {} # dict of interfaces to ip addresses as strings
self.macs = {} # dict of interfacesto mac addresses as strings
self.connection = {} # remote node connected to each interface
self.nameToIntf = {} # dict of interface names to Intfs
self.execed = False
self.lastCmd = None
self.lastPid = None
......@@ -172,7 +170,7 @@ def sendCmd( self, *args, **kwargs ):
if len( args ) > 0:
cmd = args
if not isinstance( cmd, str ):
cmd = ' '.join( cmd )
cmd = ' '.join( [ str( c ) for c in cmd ] )
if not re.search( r'\w', cmd ):
# Replace empty commands with something harmless
cmd = 'echo -n'
......@@ -254,10 +252,6 @@ def cmdPrint( self, *args):
# the real interfaces are created as veth pairs, so we can't
# make a single interface at a time.
def intfName( self, n ):
"Construct a canonical interface name node-ethN for interface n."
return self.name + '-eth' + repr( n )
def newPort( self ):
"Return the next port number to allocate."
if len( self.ports ) > 0:
......@@ -266,56 +260,45 @@ def newPort( self ):
def addIntf( self, intf, port=None ):
"""Add an interface.
intf: interface name (e.g. nodeN-ethM)
intf: interface
port: port number (optional, typically OpenFlow port number)"""
if port is None:
port = self.newPort()
self.intfs[ port ] = intf
self.ports[ intf ] = port
#info( '\n' )
#info( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) )
self.nameToIntf[ intf.name ] = intf
info( '\n' )
info( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) )
if self.inNamespace:
#info( 'moving w/inNamespace set\n' )
moveIntf( intf, self )
info( 'moving', intf, 'into namespace for', self.name, '\n' )
moveIntf( intf.name, self )
def registerIntf( self, intf, dstNode, dstIntf ):
"Register connection of intf to dstIntf on dstNode."
self.connection[ intf ] = ( dstNode, dstIntf )
def defaultIntf( self ):
"Return interface for lowest port"
ports = self.intfs.keys()
if ports:
return self.intfs[ min( ports ) ]
def connectionsTo( self, node):
"Return [(srcIntf, dstIntf)..] for connections to dstNode."
def intf( self, intf='' ):
"""Return our interface object with given name,x
or default intf if name is empty"""
if not intf:
return self.defaultIntf()
elif type( intf) is str:
return self.nameToIntf[ intf ]
else:
return intf
def linksTo( self, node):
"Return [ link1, link2...] for all links from self to node."
# We could optimize this if it is important
connections = []
for intf in self.connection.keys():
dstNode, dstIntf = self.connection[ intf ]
if dstNode == node:
connections.append( ( intf, dstIntf ) )
return connections
# This is a symmetric operation, but it makes sense to put
# the code here since it is tightly coupled to routines in
# this class. For a more symmetric API, you can use
# mininet.util.createLink()
def linkTo( self, node2, port1=None, port2=None ):
"""Create link to another node, making two new interfaces.
node2: Node to link us to
port1: our port number (optional)
port2: node2 port number (optional)
returns: intf1 name, intf2 name"""
node1 = self
if port1 is None:
port1 = node1.newPort()
if port2 is None:
port2 = node2.newPort()
intf1 = node1.intfName( port1 )
intf2 = node2.intfName( port2 )
makeIntfPair( intf1, intf2 )
node1.addIntf( intf1, port1 )
node2.addIntf( intf2, port2 )
node1.registerIntf( intf1, node2, intf2 )
node2.registerIntf( intf2, node1, intf1 )
return intf1, intf2
links = []
for intf in self.intfs:
link = intf.link
nodes = ( link.intf1.node, link.intf2.node )
if self in nodes and node in nodes:
links.append( link )
return links
def deleteIntfs( self ):
"Delete all of our interfaces."
......@@ -325,18 +308,10 @@ def deleteIntfs( self ):
# have been removed by the kernel. Unfortunately this is very slow,
# at least with Linux kernels before 2.6.33
for intf in self.intfs.values():
quietRun( 'ip link del ' + intf )
intf.delete()
info( '.' )
# Does it help to sleep to let things run?
sleep( 0.001 )
def setMAC( self, intf, mac ):
"""Set the MAC address for an interface.
mac: MAC address as string"""
result = self.cmd( 'ifconfig', intf, 'down' )
result += self.cmd( 'ifconfig', intf, 'hw', 'ether', mac )
result += self.cmd( 'ifconfig', intf, 'up' )
return result
# Routing support
def setARP( self, ip, mac ):
"""Add an ARP entry.
......@@ -345,16 +320,6 @@ def setARP( self, ip, mac ):
result = self.cmd( 'arp', '-s', ip, mac )
return result
def setIP( self, intf, ip, prefixLen=8 ):
"""Set the IP address for an interface.
intf: interface name
ip: IP address as a string
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
ipSub = '%s/%d' % ( ip, prefixLen )
result = self.cmd( 'ifconfig', intf, ipSub, 'up' )
self.ips[ intf ] = ip
return result
def setHostRoute( self, ip, intf ):
"""Add route to host.
ip: IP address as dotted decimal
......@@ -365,62 +330,52 @@ def setDefaultRoute( self, intf ):
"""Set the default route to go through intf.
intf: string, interface name"""
self.cmd( 'ip route flush root 0/0' )
return self.cmd( 'route add default ' + intf )
return self.cmd( 'route add default %s' % intf )
def defaultIntf( self ):
"Return interface for lowest port"
ports = self.intfs.keys()
if ports:
return self.intfs[ min( ports ) ]
# Convenience methods
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
_macMatchRegex = re.compile( r'..:..:..:..:..:..' )
def setMAC( self, mac, intf=''):
"""Set the MAC address for an interface.
intf: intf or intf name
mac: MAC address as string"""
return self.intf( intf ).setMAC( mac )
def setIP( self, ip, prefixLen=8, intf='' ):
"""Set the IP address for an interface.
intf: interface name
ip: IP address as a string
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
# This should probably be rethought:
ipSub = '%s/%s' % ( ip, prefixLen )
return self.intf( intf ).setIP( ipSub )
def IP( self, intf=None ):
"Return IP address of a node or specific interface."
if intf is None:
intf = self.defaultIntf()
if intf and not self.waiting:
self.updateIP( intf )
return self.ips.get( intf, None )
return self.intf( intf ).IP()
def MAC( self, intf=None ):
"Return MAC address of a node or specific interface."
if intf is None:
intf = self.defaultIntf()
if intf and not self.waiting:
self.updateMAC( intf )
return self.macs.get( intf, None )
def updateIP( self, intf ):
"Update IP address for an interface"
assert not self.waiting
ifconfig = self.cmd( 'ifconfig ' + intf )
ips = self._ipMatchRegex.findall( ifconfig )
if ips:
self.ips[ intf ] = ips[ 0 ]
else:
self.ips[ intf ] = None
return self.intf( intf ).MAC()
def updateMAC( self, intf ):
"Update MAC address for an interface"
assert not self.waiting
ifconfig = self.cmd( 'ifconfig ' + intf )
macs = self._macMatchRegex.findall( ifconfig )
if macs:
self.macs[ intf ] = macs[ 0 ]
else:
self.macs[ intf ] = None
def intfIsUp( self, intf ):
def intfIsUp( self, intf=None ):
"Check if an interface is up."
return 'UP' in self.cmd( 'ifconfig ' + intf )
return self.intf( intf ).isUp()
# This is here for backward compatibility
def linkTo( self, node, link=Link ):
"""(Deprecated) Link to another node
replace with Link( node1, node2)"""
return link( self, node )
# Other methods
def intfNames( self ):
"The names of our interfaces"
return [ str( i ) for i in sorted( self.ports.values() ) ]
def __str__( self ):
intfs = sorted( self.intfs.values() )
return '%s: IP=%s intfs=%s pid=%s' % (
self.name, self.IP(), ','.join( intfs ), self.pid )
self.name, self.IP(), ','.join( self.intfNames() ), self.pid )
class Host( Node ):
......@@ -623,10 +578,9 @@ class OVSSwitch( Switch ):
def __init__( self, name, dp=None, **kwargs ):
"""Init.
name: name for switch
dp: netlink id (0, 1, 2, ...)
defaultMAC: default MAC as unsigned int; random value if None"""
Switch.__init__( self, name, **kwargs )
self.dp = 'dp%i' % dp
self.dp = name
@staticmethod
def setup():
......@@ -671,7 +625,6 @@ def stop( self ):
OVSKernelSwitch = OVSSwitch
class Controller( Node ):
"""A Controller is a Node that is running (or has execed?) an
OpenFlow controller."""
......@@ -704,8 +657,9 @@ def stop( self ):
def IP( self, intf=None ):
"Return IP address of the Controller"
ip = Node.IP( self, intf=intf )
if ip is None:
if self.intfs:
ip = Node.IP( self, intf )
else:
ip = self.defaultIP
return ip
......
......@@ -16,7 +16,14 @@
# from networkx.classes.graph import Graph
from networkx import Graph
from mininet.node import SWITCH_PORT_BASE
from mininet.node import SWITCH_PORT_BASE, Host, OVSSwitch
from mininet.link import Link
# BL: it's hard to figure out how to do this right yet remain flexible
# These classes will be used as the defaults if no class is passed
# into either Topo() or Node()
TopoDefaultNode = Host
TopoDefaultSwitch = OVSSwitch
class NodeID(object):
'''Topo node identifier.'''
......@@ -54,11 +61,12 @@ def ip_str(self):
return "10.%i.%i.%i" % (hi, mid, lo)
class Node(object):
class Node( object ):
'''Node-specific vertex metadata for a Topo object.'''
def __init__(self, connected = False, admin_on = True,
power_on = True, fault = False, is_switch = True):
def __init__(self, connected=False, admin_on=True,
power_on=True, fault=False, is_switch=True,
cls=None, **params ):
'''Init.
@param connected actively connected to controller
......@@ -66,18 +74,26 @@ def __init__(self, connected = False, admin_on = True,
@param power_on powered on or off
@param fault fault seen on node
@param is_switch switch or host
@param cls node class (e.g. Host, Switch)
@param params node parameters
'''
self.connected = connected
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
self.is_switch = is_switch
# Above should be deleted and replaced by the following
# BL: is_switch is a bit annoying if we can just specify
# the node class instead!!
self.cls = cls if cls else ( TopoDefaultSwitch if is_switch else TopoDefaultNode )
self.params = params if params else {}
class Edge(object):
'''Edge-specific metadata for a StructuredTopo graph.'''
def __init__(self, admin_on = True, power_on = True, fault = False):
def __init__(self, admin_on=True, power_on=True, fault=False,
cls=Link, **params):
'''Init.
@param admin_on administratively on or off; defaults to True
......@@ -87,31 +103,40 @@ def __init__(self, admin_on = True, power_on = True, fault = False):
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
# Above should be deleted and replaced by the following
self.cls = cls
self.params = params
class Topo(object):
'''Data center network representation for structured multi-trees.'''
def __init__(self):
'''Create Topo object.
'''
def __init__(self, node=Host, switch=None, link=Link):
"""Create Topo object.
node: default node/host class
switch: default switch class
Link: default link class"""
self.g = Graph()
self.node_info = {} # dpids hash to Node objects
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
self.ports = {} # ports[src][dst] is port on src that connects to dst
self.id_gen = NodeID # class used to generate dpid
self.node = node
self.switch = switch
self.link = link
def add_node(self, dpid, node):
def add_node(self, dpid, node=None):
'''Add Node to graph.
@param dpid dpid
@param node Node object
'''
self.g.add_node(dpid)
if not node:
node = Node( link=self.link )
self.node_info[dpid] = node
def add_edge(self, src, dst, edge = None):
def add_edge(self, src, dst, edge=None):
'''Add edge (Node, Node) to graph.
@param src src dpid
......@@ -121,7 +146,7 @@ def add_edge(self, src, dst, edge = None):
src, dst = tuple(sorted([src, dst]))
self.g.add_edge(src, dst)
if not edge:
edge = Edge()
edge = Edge( link=self.link )
self.edge_info[(src, dst)] = edge
self.add_port(src, dst)
......@@ -276,6 +301,12 @@ def port(self, src, dst):
assert dst in self.ports and src in self.ports[dst]
return (self.ports[src][dst], self.ports[dst][src])
def edgeInfo( self, src, dst ):
"Return edge metadata"
# BL: Perhaps this should be rethought or we should just use the
# dicts...
return self.edge_info[ ( src, dst ) ]
def enable_edges(self):
'''Enable all edges in the network graph.
......@@ -321,7 +352,12 @@ def ip(self, dpid):
'''
return self.id_gen(dpid = dpid).ip_str()
def nodeInfo( self, dpid ):
"Return metadata for node"
# BL: may wish to rethink this or just use dicts..
return self.node_info[ dpid ]
class SingleSwitchTopo(Topo):
'''Single switch connected to k hosts.'''
......
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