Attempt at revised/simplified topo class:
- keys are strings - metadata is simply a dict - buildFromTopo greatly simplified
This commit is contained in:
+42
-56
@@ -97,7 +97,7 @@ from mininet.log import info, error, debug, output
|
|||||||
from mininet.node import Host, OVSKernelSwitch, Controller
|
from mininet.node import Host, OVSKernelSwitch, Controller
|
||||||
from mininet.link import Link
|
from mininet.link import Link
|
||||||
from mininet.util import quietRun, fixLimits
|
from mininet.util import quietRun, fixLimits
|
||||||
from mininet.util import macColonHex, ipStr, ipParse, netParse
|
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
|
||||||
from mininet.term import cleanUpScreens, makeTerms
|
from mininet.term import cleanUpScreens, makeTerms
|
||||||
|
|
||||||
class Mininet( object ):
|
class Mininet( object ):
|
||||||
@@ -131,6 +131,8 @@ class Mininet( object ):
|
|||||||
self.link = link
|
self.link = link
|
||||||
self.intf = intf
|
self.intf = intf
|
||||||
self.ipBase = ipBase
|
self.ipBase = ipBase
|
||||||
|
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
|
||||||
|
self.nextIP = 1 # start for address allocation
|
||||||
self.inNamespace = inNamespace
|
self.inNamespace = inNamespace
|
||||||
self.xterms = xterms
|
self.xterms = xterms
|
||||||
self.cleanup = cleanup
|
self.cleanup = cleanup
|
||||||
@@ -143,7 +145,6 @@ class Mininet( object ):
|
|||||||
self.controllers = []
|
self.controllers = []
|
||||||
|
|
||||||
self.nameToNode = {} # name to Node (Host/Switch) objects
|
self.nameToNode = {} # name to Node (Host/Switch) objects
|
||||||
self.idToNode = {} # dpid to Node (Host/Switch) objects
|
|
||||||
|
|
||||||
self.terms = [] # list of spawned xterm processes
|
self.terms = [] # list of spawned xterm processes
|
||||||
|
|
||||||
@@ -153,35 +154,39 @@ class Mininet( object ):
|
|||||||
if topo and build:
|
if topo and build:
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
# BL Note:
|
def addHost( self, name, cls=None, **params ):
|
||||||
# The specific items for host/switch/etc. should probably be
|
|
||||||
# handled in the node classes rather than here!!
|
|
||||||
|
|
||||||
def addHost( self, name, host=None, **params ):
|
|
||||||
"""Add host.
|
"""Add host.
|
||||||
name: name of host to add
|
name: name of host to add
|
||||||
host: custom host constructor (optional)
|
cls: custom host class/constructor (optional)
|
||||||
params: parameters for host
|
params: parameters for host
|
||||||
returns: added host"""
|
returns: added host"""
|
||||||
if not host:
|
# Default IP and MAC addresses
|
||||||
host = self.host
|
defaults = { 'ip': ipAdd( self.nextIP,
|
||||||
h = host( name, **params)
|
ipBaseNum=self.ipBaseNum,
|
||||||
|
prefixLen=self.prefixLen ) }
|
||||||
|
if self.autoSetMacs:
|
||||||
|
defaults[ 'mac'] = macColonHex( self.nextIP )
|
||||||
|
self.nextIP += 1
|
||||||
|
defaults.update( params )
|
||||||
|
if not cls:
|
||||||
|
cls = self.host
|
||||||
|
h = cls( name, **defaults )
|
||||||
self.hosts.append( h )
|
self.hosts.append( h )
|
||||||
self.nameToNode[ name ] = h
|
self.nameToNode[ name ] = h
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def addSwitch( self, name, switch=None, **params ):
|
def addSwitch( self, name, cls=None, **params ):
|
||||||
"""Add switch.
|
"""Add switch.
|
||||||
name: name of switch to add
|
name: name of switch to add
|
||||||
switch: custom switch constructor (optional)
|
cls: custom switch class/constructor (optional)
|
||||||
returns: added switch
|
returns: added switch
|
||||||
side effect: increments listenPort ivar ."""
|
side effect: increments listenPort ivar ."""
|
||||||
defaults = { 'listenPort': self.listenPort,
|
defaults = { 'listenPort': self.listenPort,
|
||||||
'inNamespace': self.inNamespace }
|
'inNamespace': self.inNamespace }
|
||||||
defaults.update( params )
|
defaults.update( params )
|
||||||
if not switch:
|
if not cls:
|
||||||
switch = self.switch
|
cls = self.switch
|
||||||
sw = self.switch( name, **defaults )
|
sw = cls( name, **defaults )
|
||||||
if not self.inNamespace and self.listenPort:
|
if not self.inNamespace and self.listenPort:
|
||||||
self.listenPort += 1
|
self.listenPort += 1
|
||||||
self.switches.append( sw )
|
self.switches.append( sw )
|
||||||
@@ -199,6 +204,15 @@ class Mininet( object ):
|
|||||||
self.nameToNode[ name ] = controller_new
|
self.nameToNode[ name ] = controller_new
|
||||||
return controller_new
|
return controller_new
|
||||||
|
|
||||||
|
def addLink( self, src, dst, srcPort=None, dstPort=None,
|
||||||
|
cls=None, **params ):
|
||||||
|
"Add a link from topo"
|
||||||
|
if self.intf and not 'intf' in params:
|
||||||
|
params[ 'intf' ] = self.intf
|
||||||
|
if not cls:
|
||||||
|
cls = self.link
|
||||||
|
return cls( src, dst, srcPort, dstPort, **params )
|
||||||
|
|
||||||
def configHosts( self ):
|
def configHosts( self ):
|
||||||
"Configure a set of hosts."
|
"Configure a set of hosts."
|
||||||
for host in self.hosts:
|
for host in self.hosts:
|
||||||
@@ -215,40 +229,6 @@ class Mininet( object ):
|
|||||||
At the end of this function, everything should be connected
|
At the end of this function, everything should be connected
|
||||||
and up."""
|
and up."""
|
||||||
|
|
||||||
ipBaseNum, prefixLen = netParse( self.ipBase )
|
|
||||||
|
|
||||||
if not topo:
|
|
||||||
topo = self.topo()
|
|
||||||
|
|
||||||
def addNode( prefix, addMethod, nodeId ):
|
|
||||||
"Add a host or a switch from topo"
|
|
||||||
name = prefix + topo.name( nodeId )
|
|
||||||
ni = topo.nodeInfo( nodeId )
|
|
||||||
# Default IP and MAC addresses
|
|
||||||
defaults = { 'ip': topo.ip( nodeId,
|
|
||||||
ipBaseNum=ipBaseNum,
|
|
||||||
prefixLen=prefixLen ) }
|
|
||||||
if self.autoSetMacs:
|
|
||||||
defaults[ 'mac'] = macColonHex( nodeId )
|
|
||||||
defaults.update( ni.params )
|
|
||||||
node = addMethod( name, cls=ni.cls, **defaults )
|
|
||||||
self.idToNode[ nodeId ] = node
|
|
||||||
info( name + ' ' )
|
|
||||||
|
|
||||||
def addLink( srcId, dstId, link=None ):
|
|
||||||
"Add a link from topo"
|
|
||||||
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
|
|
||||||
srcPort, dstPort = topo.port( srcId, dstId )
|
|
||||||
ei = topo.edgeInfo( srcId, dstId )
|
|
||||||
link = getattr( ei, 'cls', link )
|
|
||||||
params = ei.params
|
|
||||||
if self.intf and not 'intf' in params:
|
|
||||||
params[ 'intf' ] = self.intf
|
|
||||||
if not link:
|
|
||||||
link = self.link
|
|
||||||
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
|
||||||
link( src, dst, srcPort, dstPort, **params )
|
|
||||||
|
|
||||||
# Possibly we should clean up here and/or validate
|
# Possibly we should clean up here and/or validate
|
||||||
# the topo
|
# the topo
|
||||||
if self.cleanup:
|
if self.cleanup:
|
||||||
@@ -262,16 +242,22 @@ class Mininet( object ):
|
|||||||
self.addController( 'c0' )
|
self.addController( 'c0' )
|
||||||
|
|
||||||
info( '*** Adding hosts:\n' )
|
info( '*** Adding hosts:\n' )
|
||||||
for hostId in sorted( topo.hosts() ):
|
for hostName in topo.hosts():
|
||||||
addNode( 'h', self.addHost, hostId )
|
self.addHost( hostName, **topo.nodeInfo( hostName ) )
|
||||||
|
info( hostName + ' ' )
|
||||||
|
|
||||||
info( '\n*** Adding switches:\n' )
|
info( '\n*** Adding switches:\n' )
|
||||||
for switchId in sorted( topo.switches() ):
|
for switchName in topo.switches():
|
||||||
addNode( 's', self.addSwitch, switchId )
|
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
|
||||||
|
info( switchName + ' ' )
|
||||||
|
|
||||||
info( '\n*** Adding links:\n' )
|
info( '\n*** Adding links:\n' )
|
||||||
for srcId, dstId in sorted( topo.edges() ):
|
for srcName, dstName in topo.links(sort=True):
|
||||||
addLink( srcId, dstId )
|
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
|
||||||
|
srcPort, dstPort = topo.port( srcName, dstName )
|
||||||
|
self.addLink( src, dst, srcPort, dstPort,
|
||||||
|
**topo.linkInfo( srcName, dstName ) )
|
||||||
|
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
||||||
|
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
|
|||||||
+114
-317
@@ -16,146 +16,73 @@ setup for testing, and can even be emulated with the Mininet package.
|
|||||||
# from networkx.classes.graph import Graph
|
# from networkx.classes.graph import Graph
|
||||||
|
|
||||||
from networkx import Graph
|
from networkx import Graph
|
||||||
from mininet.util import netParse, ipStr
|
from mininet.util import netParse, ipStr, irange, natural, naturalSeq
|
||||||
|
|
||||||
class NodeID(object):
|
|
||||||
'''Topo node identifier.'''
|
|
||||||
|
|
||||||
def __init__(self, dpid = None):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param dpid dpid
|
|
||||||
'''
|
|
||||||
# DPID-compatible hashable identifier: opaque 64-bit unsigned int
|
|
||||||
self.dpid = dpid
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
'''String conversion.
|
|
||||||
|
|
||||||
@return str dpid as string
|
|
||||||
'''
|
|
||||||
return str(self.dpid)
|
|
||||||
|
|
||||||
def name_str(self):
|
|
||||||
'''Name conversion.
|
|
||||||
|
|
||||||
@return name name as string
|
|
||||||
'''
|
|
||||||
return str(self.dpid)
|
|
||||||
|
|
||||||
def ip_str(self, ipBase=None, prefixLen=8, ipBaseNum=0x0a000000):
|
|
||||||
'''Name conversion.
|
|
||||||
ipBase: optional base IP address string
|
|
||||||
prefixLen: optional IP prefix length
|
|
||||||
ipBaseNum: option base IP address as int
|
|
||||||
@return ip ip as string
|
|
||||||
'''
|
|
||||||
if ipBase:
|
|
||||||
ipnum, prefixLen = netParse( ipBase )
|
|
||||||
else:
|
|
||||||
ipBaseNum = ipBaseNum
|
|
||||||
# Ugly but functional
|
|
||||||
assert self.dpid < ( 1 << ( 32 - prefixLen ) )
|
|
||||||
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
|
|
||||||
ipnum = self.dpid + ( ipBaseNum & mask )
|
|
||||||
return ipStr( ipnum )
|
|
||||||
|
|
||||||
|
|
||||||
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,
|
|
||||||
cls=None, **params ):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param connected actively connected to controller
|
|
||||||
@param admin_on administratively on or off
|
|
||||||
@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
|
|
||||||
# BL: Above should mostly be deleted and replaced by the following
|
|
||||||
# is_switch is a bit annoying if we are already specifying
|
|
||||||
# a switch class!! Except that if cls is not specified,
|
|
||||||
# then Mininet() knows whether to create a switch or a host
|
|
||||||
# node and can call its own constructors...
|
|
||||||
self.cls = cls
|
|
||||||
self.params = params
|
|
||||||
|
|
||||||
|
|
||||||
class Edge(object):
|
|
||||||
'''Edge-specific metadata for a StructuredTopo graph.'''
|
|
||||||
|
|
||||||
def __init__(self, admin_on=True, power_on=True, fault=False,
|
|
||||||
cls=None, **params):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param admin_on administratively on or off; defaults to True
|
|
||||||
@param power_on powered on or off; defaults to True
|
|
||||||
@param fault fault seen on edge; defaults to 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):
|
class Topo(object):
|
||||||
"""Data center network representation for structured multi-trees.
|
"Data center network representation for structured multi-trees."
|
||||||
Note that the order of precedence is:
|
|
||||||
per-node/link classes and parameters
|
|
||||||
per-topo classes
|
|
||||||
per-network classes"""
|
|
||||||
|
|
||||||
def __init__(self, node=None, switch=None, link=None ):
|
def __init__(self, hopts=None, sopts=None, lopts=None):
|
||||||
"""Create Topo object.
|
"""Topo object:
|
||||||
node: default node/host class (optional)
|
hinfo: default host options
|
||||||
switch: default switch class (optional)
|
sopts: default switch options
|
||||||
link: default link class (optional)
|
lopts: default link options"""
|
||||||
ipBase: default IP address base (optional)"""
|
|
||||||
self.g = Graph()
|
self.g = Graph()
|
||||||
self.node_info = {} # dpids hash to Node objects
|
self.node_info = {}
|
||||||
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
|
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
|
||||||
|
self.hopts = {} if hopts is None else hopts
|
||||||
|
self.sopts = {} if sopts is None else lopts
|
||||||
|
self.lopts = {} if lopts is None else lopts
|
||||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
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=None):
|
def add_node(self, name, *args, **opts):
|
||||||
'''Add Node to graph.
|
"""Add Node to graph.
|
||||||
|
add_node('name', dict) <or> add_node('name', **opts)
|
||||||
|
name: name
|
||||||
|
args: dict of node options
|
||||||
|
opts: node options"""
|
||||||
|
self.g.add_node(name)
|
||||||
|
if args and type(args[0]) is dict:
|
||||||
|
opts = args[0]
|
||||||
|
self.node_info[name] = opts
|
||||||
|
return name
|
||||||
|
|
||||||
@param dpid dpid
|
def add_host(self, name, *args, **opts):
|
||||||
@param node Node object
|
"""Convenience method: Add host to graph.
|
||||||
'''
|
add_host('name', dict) <or> add_host('name', **opts)
|
||||||
self.g.add_node(dpid)
|
name: name
|
||||||
if not node:
|
args: dict of node options
|
||||||
node = Node( link=self.link )
|
opts: node options"""
|
||||||
self.node_info[dpid] = node
|
if not opts and self.hopts:
|
||||||
|
opts = self.hopts
|
||||||
|
return self.add_node(name, *args, **opts)
|
||||||
|
|
||||||
def add_edge(self, src, dst, edge=None):
|
def add_switch(self, name, **opts):
|
||||||
'''Add edge (Node, Node) to graph.
|
"""Convenience method: Add switch to graph.
|
||||||
|
add_switch('name', dict) <or> add_switch('name', **opts)
|
||||||
|
name: name
|
||||||
|
args: dict of node options
|
||||||
|
opts: node options"""
|
||||||
|
if not opts and self.sopts:
|
||||||
|
opts = self.sopts
|
||||||
|
result = self.add_node(name, is_switch=True, **opts)
|
||||||
|
return result
|
||||||
|
|
||||||
@param src src dpid
|
def add_link(self, src, dst, *args, **opts):
|
||||||
@param dst dst dpid
|
"""Add link (Node, Node) to topo.
|
||||||
@param edge Edge object
|
add_link(src, dst, dict) <or> add_link(src, dst, **opts)
|
||||||
'''
|
src: src name
|
||||||
src, dst = tuple(sorted([src, dst]))
|
dst: dst name
|
||||||
|
args: dict of node options
|
||||||
|
params: link parameters"""
|
||||||
|
src, dst = sorted([src, dst], key=naturalSeq)
|
||||||
self.g.add_edge(src, dst)
|
self.g.add_edge(src, dst)
|
||||||
if not edge:
|
if args and type(args[0]) is dict:
|
||||||
edge = Edge( cls=self.link )
|
opts = args[0]
|
||||||
self.edge_info[(src, dst)] = edge
|
if not opts and self.sopts:
|
||||||
|
opts = self.sopts
|
||||||
|
self.link_info[(src, dst)] = opts
|
||||||
self.add_port(src, dst)
|
self.add_port(src, dst)
|
||||||
|
return src, dst
|
||||||
|
|
||||||
def add_port(self, src, dst):
|
def add_port(self, src, dst):
|
||||||
'''Generate port mapping for new edge.
|
'''Generate port mapping for new edge.
|
||||||
@@ -175,131 +102,48 @@ class Topo(object):
|
|||||||
if src not in self.ports[dst]:
|
if src not in self.ports[dst]:
|
||||||
# num outlinks
|
# num outlinks
|
||||||
self.ports[dst][src] = len(self.ports[dst]) + dst_base
|
self.ports[dst][src] = len(self.ports[dst]) + dst_base
|
||||||
|
|
||||||
def node_enabled(self, dpid):
|
def nodes(self, sort=True):
|
||||||
'''Is node connected, admin on, powered on, and fault-free?
|
"Return nodes in graph"
|
||||||
|
if sort:
|
||||||
@param dpid dpid
|
return sorted( self.g.nodes(), key=natural )
|
||||||
|
|
||||||
@return bool node is enabled
|
|
||||||
'''
|
|
||||||
ni = self.node_info[dpid]
|
|
||||||
return ni.connected and ni.admin_on and ni.power_on and not ni.fault
|
|
||||||
|
|
||||||
def nodes_enabled(self, dpids, enabled = True):
|
|
||||||
'''Return subset of enabled nodes
|
|
||||||
|
|
||||||
@param dpids list of dpids
|
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids filtered list of dpids
|
|
||||||
'''
|
|
||||||
if enabled:
|
|
||||||
return [n for n in dpids if self.node_enabled(n)]
|
|
||||||
else:
|
else:
|
||||||
return dpids
|
return self.g.nodes()
|
||||||
|
|
||||||
def nodes(self, enabled = True):
|
|
||||||
'''Return graph nodes.
|
|
||||||
|
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
|
||||||
'''
|
|
||||||
return self.nodes_enabled(self.g.nodes(), enabled)
|
|
||||||
|
|
||||||
def nodes_str(self, dpids):
|
|
||||||
'''Return string of custom-encoded nodes.
|
|
||||||
|
|
||||||
@param dpids list of dpids
|
|
||||||
|
|
||||||
@return str string
|
|
||||||
'''
|
|
||||||
return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
|
|
||||||
|
|
||||||
def is_switch(self, n):
|
def is_switch(self, n):
|
||||||
'''Returns true if node is a switch.'''
|
'''Returns true if node is a switch.'''
|
||||||
return self.node_info[n].is_switch
|
info = self.node_info[n]
|
||||||
|
return info and info['is_switch']
|
||||||
|
|
||||||
def switches(self, enabled = True):
|
def switches(self, sort=True):
|
||||||
'''Return switches.
|
'''Return switches.
|
||||||
|
sort: sort switches alphabetically
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
@return dpids list of dpids
|
||||||
'''
|
'''
|
||||||
nodes = [n for n in self.g.nodes() if self.is_switch(n)]
|
return [n for n in self.nodes(sort) if self.is_switch(n)]
|
||||||
return self.nodes_enabled(nodes, enabled)
|
|
||||||
|
|
||||||
def hosts(self, enabled = True):
|
def hosts(self, sort=True):
|
||||||
'''Return hosts.
|
'''Return hosts.
|
||||||
|
sort: sort hosts alphabetically
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
@return dpids list of dpids
|
||||||
'''
|
'''
|
||||||
|
return [n for n in self.nodes(sort) if not self.is_switch(n)]
|
||||||
|
|
||||||
def is_host(n):
|
def links(self, sort=True):
|
||||||
'''Returns true if node is a host.'''
|
'''Return links.
|
||||||
return not self.node_info[n].is_switch
|
sort: sort links alphabetically
|
||||||
|
@return links list of name pairs
|
||||||
nodes = [n for n in self.g.nodes() if is_host(n)]
|
|
||||||
return self.nodes_enabled(nodes, enabled)
|
|
||||||
|
|
||||||
def edge_enabled(self, edge):
|
|
||||||
'''Is edge admin on, powered on, and fault-free?
|
|
||||||
|
|
||||||
@param edge (src, dst) dpid tuple
|
|
||||||
|
|
||||||
@return bool edge is enabled
|
|
||||||
'''
|
'''
|
||||||
src, dst = edge
|
if not sort:
|
||||||
src, dst = tuple(sorted([src, dst]))
|
return self.g.edges()
|
||||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
|
||||||
return ei.admin_on and ei.power_on and not ei.fault
|
|
||||||
|
|
||||||
def edges_enabled(self, edges, enabled = True):
|
|
||||||
'''Return subset of enabled edges
|
|
||||||
|
|
||||||
@param edges list of edges
|
|
||||||
@param enabled only return enabled edges?
|
|
||||||
|
|
||||||
@return edges filtered list of edges
|
|
||||||
'''
|
|
||||||
if enabled:
|
|
||||||
return [e for e in edges if self.edge_enabled(e)]
|
|
||||||
else:
|
else:
|
||||||
return edges
|
return sorted( self.g.edges(), key=naturalSeq )
|
||||||
|
|
||||||
def edges(self, enabled = True):
|
|
||||||
'''Return edges.
|
|
||||||
|
|
||||||
@param enabled only return enabled edges?
|
|
||||||
|
|
||||||
@return edges list of dpid pairs
|
|
||||||
'''
|
|
||||||
return self.edges_enabled(self.g.edges(), enabled)
|
|
||||||
|
|
||||||
def edges_str(self, dpid_pairs):
|
|
||||||
'''Return string of custom-encoded node pairs.
|
|
||||||
|
|
||||||
@param dpid_pairs list of dpid pairs (src, dst)
|
|
||||||
|
|
||||||
@return str string
|
|
||||||
'''
|
|
||||||
edges = []
|
|
||||||
for pair in dpid_pairs:
|
|
||||||
src, dst = pair
|
|
||||||
src = str(self.id_gen(dpid = src))
|
|
||||||
dst = str(self.id_gen(dpid = dst))
|
|
||||||
edges.append((src, dst))
|
|
||||||
return edges
|
|
||||||
|
|
||||||
def port(self, src, dst):
|
def port(self, src, dst):
|
||||||
'''Get port number.
|
'''Get port number.
|
||||||
|
|
||||||
@param src source switch DPID
|
@param src source switch name
|
||||||
@param dst destination switch DPID
|
@param dst destination switch name
|
||||||
@return tuple (src_port, dst_port):
|
@return tuple (src_port, dst_port):
|
||||||
src_port: port on source switch leading to the destination switch
|
src_port: port on source switch leading to the destination switch
|
||||||
dst_port: port on destination switch leading to the source switch
|
dst_port: port on destination switch leading to the source switch
|
||||||
@@ -308,84 +152,41 @@ class Topo(object):
|
|||||||
assert dst in self.ports and src in self.ports[dst]
|
assert dst in self.ports and src in self.ports[dst]
|
||||||
return (self.ports[src][dst], self.ports[dst][src])
|
return (self.ports[src][dst], self.ports[dst][src])
|
||||||
|
|
||||||
def edgeInfo( self, src, dst ):
|
def linkInfo( self, src, dst ):
|
||||||
"Return edge metadata"
|
"Return link metadata"
|
||||||
# BL: Perhaps this should be rethought or we should just use the
|
src, dst = sorted((src, dst), key=naturalSeq)
|
||||||
# dicts...
|
return self.link_info[(src, dst)]
|
||||||
return self.edge_info[ ( src, dst ) ]
|
|
||||||
|
|
||||||
def enable_edges(self):
|
def nodeInfo( self, name ):
|
||||||
'''Enable all edges in the network graph.
|
"Return metadata (dict) for node"
|
||||||
|
info = self.node_info[ name ]
|
||||||
|
return info if info is not None else {}
|
||||||
|
|
||||||
Set admin on, power on, and fault off.
|
def setNodeInfo( self, name, info ):
|
||||||
'''
|
self.node_info[ name ] = info
|
||||||
for e in self.g.edges():
|
|
||||||
src, dst = e
|
|
||||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
|
||||||
ei.admin_on = True
|
|
||||||
ei.power_on = True
|
|
||||||
ei.fault = False
|
|
||||||
|
|
||||||
def enable_nodes(self):
|
|
||||||
'''Enable all nodes in the network graph.
|
|
||||||
|
|
||||||
Set connected on, admin on, power on, and fault off.
|
|
||||||
'''
|
|
||||||
for node in self.g.nodes():
|
|
||||||
ni = self.node_info[node]
|
|
||||||
ni.connected = True
|
|
||||||
ni.admin_on = True
|
|
||||||
ni.power_on = True
|
|
||||||
ni.fault = False
|
|
||||||
|
|
||||||
def enable_all(self):
|
|
||||||
'''Enable all nodes and edges in the network graph.'''
|
|
||||||
self.enable_nodes()
|
|
||||||
self.enable_edges()
|
|
||||||
|
|
||||||
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, **params):
|
|
||||||
'''Get IP dotted-decimal string of node ID.
|
|
||||||
@param dpid DPID of host or switch
|
|
||||||
@param params: params to pass to ip_str
|
|
||||||
@return ip_str
|
|
||||||
'''
|
|
||||||
return self.id_gen(dpid = dpid).ip_str(**params)
|
|
||||||
|
|
||||||
def nodeInfo( self, dpid ):
|
|
||||||
"Return metadata for node"
|
|
||||||
# BL: may wish to rethink this or just use dicts..
|
|
||||||
return self.node_info[ dpid ]
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sorted( items ):
|
||||||
|
"Items sorted in natural (i.e. alphabetical) order"
|
||||||
|
return sorted(items, key=natural)
|
||||||
|
|
||||||
class SingleSwitchTopo(Topo):
|
class SingleSwitchTopo(Topo):
|
||||||
'''Single switch connected to k hosts.'''
|
'''Single switch connected to k hosts.'''
|
||||||
|
|
||||||
def __init__(self, k = 2, enable_all = True):
|
def __init__(self, k=2, **opts):
|
||||||
'''Init.
|
'''Init.
|
||||||
|
|
||||||
@param k number of hosts
|
@param k number of hosts
|
||||||
@param enable_all enables all nodes and switches?
|
@param enable_all enables all nodes and switches?
|
||||||
'''
|
'''
|
||||||
super(SingleSwitchTopo, self).__init__()
|
super(SingleSwitchTopo, self).__init__(**opts)
|
||||||
|
|
||||||
self.k = k
|
self.k = k
|
||||||
|
|
||||||
self.add_node(1, Node())
|
switch = self.add_switch('s1')
|
||||||
hosts = range(2, k + 2)
|
for h in irange(1, k):
|
||||||
for h in hosts:
|
host = self.add_host('h%s' % h)
|
||||||
self.add_node(h, Node(is_switch = False))
|
self.add_link(host, switch)
|
||||||
self.add_edge(h, 1, Edge())
|
|
||||||
|
|
||||||
if enable_all:
|
|
||||||
self.enable_all()
|
|
||||||
|
|
||||||
|
|
||||||
class SingleSwitchReversedTopo(SingleSwitchTopo):
|
class SingleSwitchReversedTopo(SingleSwitchTopo):
|
||||||
@@ -422,27 +223,23 @@ class SingleSwitchReversedTopo(SingleSwitchTopo):
|
|||||||
|
|
||||||
|
|
||||||
class LinearTopo(Topo):
|
class LinearTopo(Topo):
|
||||||
'''Linear topology of k switches, with one host per switch.'''
|
"Linear topology of k switches, with one host per switch."
|
||||||
|
|
||||||
def __init__(self, k = 2, enable_all = True):
|
def __init__(self, k=2, **opts):
|
||||||
'''Init.
|
"""Init.
|
||||||
|
k: number of switches (and hosts)
|
||||||
|
hconf: host configuration options
|
||||||
|
lconf: link configuration options"""
|
||||||
|
|
||||||
@param k number of switches (and hosts too)
|
super(LinearTopo, self).__init__(**opts)
|
||||||
@param enable_all enables all nodes and switches?
|
|
||||||
'''
|
|
||||||
super(LinearTopo, self).__init__()
|
|
||||||
|
|
||||||
self.k = k
|
self.k = k
|
||||||
|
|
||||||
switches = range(1, k + 1)
|
lastSwitch = None
|
||||||
for s in switches:
|
for i in irange(1, k):
|
||||||
h = s + k
|
host = self.add_host('h%s' % i)
|
||||||
self.add_node(s, Node())
|
switch = self.add_switch('s%s' % i)
|
||||||
self.add_node(h, Node(is_switch = False))
|
self.add_link( host, switch)
|
||||||
self.add_edge(s, h, Edge())
|
if lastSwitch:
|
||||||
for s in switches:
|
self.add_link( switch, lastSwitch)
|
||||||
if s != k:
|
lastSwitch = switch
|
||||||
self.add_edge(s, s + 1, Edge())
|
|
||||||
|
|
||||||
if enable_all:
|
|
||||||
self.enable_all()
|
|
||||||
|
|||||||
+10
-19
@@ -1,6 +1,6 @@
|
|||||||
"Library of potentially useful topologies for Mininet"
|
"Library of potentially useful topologies for Mininet"
|
||||||
|
|
||||||
from mininet.topo import Topo, Node
|
from mininet.topo import Topo
|
||||||
from mininet.net import Mininet
|
from mininet.net import Mininet
|
||||||
|
|
||||||
class TreeTopo( Topo ):
|
class TreeTopo( Topo ):
|
||||||
@@ -8,36 +8,27 @@ class TreeTopo( Topo ):
|
|||||||
|
|
||||||
def __init__( self, depth=1, fanout=2 ):
|
def __init__( self, depth=1, fanout=2 ):
|
||||||
super( TreeTopo, self ).__init__()
|
super( TreeTopo, self ).__init__()
|
||||||
# Numbering: h1..N, sN+1..M
|
# Numbering: h1..N, s1..M
|
||||||
hostCount = fanout ** depth
|
|
||||||
self.hostNum = 1
|
self.hostNum = 1
|
||||||
self.switchNum = hostCount + 1
|
self.switchNum = 1
|
||||||
# Build topology
|
# Build topology
|
||||||
self.addTree( depth, fanout )
|
self.addTree( depth, fanout )
|
||||||
# Consider all switches and hosts 'on'
|
|
||||||
self.enable_all()
|
|
||||||
|
|
||||||
# It is OK that i is "unused" in the for loop.
|
|
||||||
# pylint: disable-msg=W0612
|
|
||||||
|
|
||||||
def addTree( self, depth, fanout ):
|
def addTree( self, depth, fanout ):
|
||||||
"""Add a subtree starting with node n.
|
"""Add a subtree starting with node n.
|
||||||
returns: last node added"""
|
returns: last node added"""
|
||||||
isSwitch = depth > 0
|
isSwitch = depth > 0
|
||||||
if isSwitch:
|
if isSwitch:
|
||||||
num = self.switchNum
|
node = self.add_switch( 's%s' % self.switchNum )
|
||||||
self.switchNum += 1
|
self.switchNum += 1
|
||||||
else:
|
for _ in range( fanout ):
|
||||||
num = self.hostNum
|
|
||||||
self.hostNum += 1
|
|
||||||
self.add_node( num, Node( is_switch=isSwitch ) )
|
|
||||||
if isSwitch:
|
|
||||||
for i in range( 0, fanout ):
|
|
||||||
child = self.addTree( depth - 1, fanout )
|
child = self.addTree( depth - 1, fanout )
|
||||||
self.add_edge( num, child )
|
self.add_link( node, child )
|
||||||
return num
|
else:
|
||||||
|
node = self.add_host( 'h%s' % self.hostNum )
|
||||||
|
self.hostNum += 1
|
||||||
|
return node
|
||||||
|
|
||||||
# pylint: enable-msg=W0612
|
|
||||||
|
|
||||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||||
"Convenience function for creating tree networks."
|
"Convenience function for creating tree networks."
|
||||||
|
|||||||
+22
-1
@@ -224,6 +224,18 @@ def ipNum( w, x, y, z ):
|
|||||||
returns: w << 24 | x << 16 | y << 8 | z"""
|
returns: w << 24 | x << 16 | y << 8 | z"""
|
||||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||||
|
|
||||||
|
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||||
|
"""Return IP address string from ints
|
||||||
|
i: int to be added to ipbase
|
||||||
|
prefixLen: optional IP prefix length
|
||||||
|
ipBaseNum: option base IP address as int
|
||||||
|
returns IP address as string"""
|
||||||
|
# Ugly but functional
|
||||||
|
assert i < ( 1 << ( 32 - prefixLen ) )
|
||||||
|
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
|
||||||
|
ipnum = i + ( ipBaseNum & mask )
|
||||||
|
return ipStr( ipnum )
|
||||||
|
|
||||||
def ipParse( ip ):
|
def ipParse( ip ):
|
||||||
"Parse an IP address and return an unsigned int."
|
"Parse an IP address and return an unsigned int."
|
||||||
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
||||||
@@ -275,9 +287,13 @@ def natural( text ):
|
|||||||
"To sort sanely/alphabetically: sorted( l, key=natural )"
|
"To sort sanely/alphabetically: sorted( l, key=natural )"
|
||||||
def num( s ):
|
def num( s ):
|
||||||
"Convert text segment to int if necessary"
|
"Convert text segment to int if necessary"
|
||||||
return int( s ) if s.isdigit() else text
|
return int( s ) if s.isdigit() else s
|
||||||
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
|
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
|
||||||
|
|
||||||
|
def naturalSeq( t ):
|
||||||
|
"Natural sort key function for sequences"
|
||||||
|
return [ natural( x ) for x in t ]
|
||||||
|
|
||||||
def numCores():
|
def numCores():
|
||||||
"Returns number of CPU cores based on /proc/cpuinfo"
|
"Returns number of CPU cores based on /proc/cpuinfo"
|
||||||
if hasattr( numCores, 'ncores' ):
|
if hasattr( numCores, 'ncores' ):
|
||||||
@@ -288,6 +304,11 @@ def numCores():
|
|||||||
return 0
|
return 0
|
||||||
return numCores.ncores
|
return numCores.ncores
|
||||||
|
|
||||||
|
def irange(start, end):
|
||||||
|
"""Inclusive range from start to end (vs. Python insanity.)
|
||||||
|
irange(1,5) -> 1, 2, 3, 4, 5"""
|
||||||
|
return range( start, end + 1 )
|
||||||
|
|
||||||
def custom( cls, **params ):
|
def custom( cls, **params ):
|
||||||
"Returns customized constructor for class cls."
|
"Returns customized constructor for class cls."
|
||||||
def customized( *args, **kwargs):
|
def customized( *args, **kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user