Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / mininet / mininet / topo.py
1 #!/usr/bin/env python
2 """@package topo
3
4 Network topology creation.
5
6 @author Brandon Heller (brandonh@stanford.edu)
7
8 This package includes code to represent network topologies.
9
10 A Topo object can be a topology database for NOX, can represent a physical
11 setup for testing, and can even be emulated with the Mininet package.
12 """
13
14 from mininet.util import irange, natural, naturalSeq
15
16 class MultiGraph( object ):
17     "Utility class to track nodes and edges - replaces networkx.MultiGraph"
18
19     def __init__( self ):
20         self.node = {}
21         self.edge = {}
22
23     def add_node( self, node, attr_dict=None, **attrs):
24         """Add node to graph
25            attr_dict: attribute dict (optional)
26            attrs: more attributes (optional)
27            warning: updates attr_dict with attrs"""
28         attr_dict = {} if attr_dict is None else attr_dict
29         attr_dict.update( attrs )
30         self.node[ node ] = attr_dict
31
32     def add_edge( self, src, dst, key=None, attr_dict=None, **attrs ):
33         """Add edge to graph
34            key: optional key
35            attr_dict: optional attribute dict
36            attrs: more attributes
37            warning: udpates attr_dict with attrs"""
38         attr_dict = {} if attr_dict is None else attr_dict
39         attr_dict.update( attrs )
40         self.node.setdefault( src, {} )
41         self.node.setdefault( dst, {} )
42         self.edge.setdefault( src, {} )
43         self.edge.setdefault( dst, {} )
44         self.edge[ src ].setdefault( dst, {} )
45         entry = self.edge[ dst ][ src ] = self.edge[ src ][ dst ]
46         # If no key, pick next ordinal number
47         if key is None:
48             keys = [ k for k in entry.keys() if isinstance( k, int ) ]
49             key = max( [ 0 ] + keys ) + 1
50         entry[ key ] = attr_dict
51         return key
52
53     def nodes( self, data=False):
54         """Return list of graph nodes
55            data: return list of ( node, attrs)"""
56         return self.node.items() if data else self.node.keys()
57
58     def edges_iter( self, data=False, keys=False ):
59         "Iterator: return graph edges, optionally with data and keys"
60         for src, entry in self.edge.items():
61             for dst, entrykeys in entry.items():
62                 if src > dst:
63                     # Skip duplicate edges
64                     continue
65                 for k, attrs in entrykeys.items():
66                     if data:
67                         if keys:
68                             yield( src, dst, k, attrs )
69                         else:
70                             yield( src, dst, attrs )
71                     else:
72                         if keys:
73                             yield( src, dst, k )
74                         else:
75                             yield( src, dst )
76
77     def edges( self, data=False, keys=False ):
78         "Return list of graph edges"
79         return list( self.edges_iter( data=data, keys=keys ) )
80
81     def __getitem__( self, node ):
82         "Return link dict for given src node"
83         return self.edge[ node ]
84
85     def __len__( self ):
86         "Return the number of nodes"
87         return len( self.node )
88
89     def convertTo( self, cls, data=False, keys=False ):
90         """Convert to a new object of networkx.MultiGraph-like class cls
91            data: include node and edge data
92            keys: include edge keys as well as edge data"""
93         g = cls()
94         g.add_nodes_from( self.nodes( data=data ) )
95         g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
96         return g
97
98
99 class Topo( object ):
100     "Data center network representation for structured multi-trees."
101
102     def __init__( self, *args, **params ):
103         """Topo object.
104            Optional named parameters:
105            hinfo: default host options
106            sopts: default switch options
107            lopts: default link options
108            calls build()"""
109         self.g = MultiGraph()
110         self.hopts = params.pop( 'hopts', {} )
111         self.sopts = params.pop( 'sopts', {} )
112         self.lopts = params.pop( 'lopts', {} )
113         # ports[src][dst][sport] is port on dst that connects to src
114         self.ports = {}
115         self.build( *args, **params )
116
117     def build( self, *args, **params ):
118         "Override this method to build your topology."
119         pass
120
121     def addNode( self, name, **opts ):
122         """Add Node to graph.
123            name: name
124            opts: node options
125            returns: node name"""
126         self.g.add_node( name, **opts )
127         return name
128
129     def addHost( self, name, **opts ):
130         """Convenience method: Add host to graph.
131            name: host name
132            opts: host options
133            returns: host name"""
134         if not opts and self.hopts:
135             opts = self.hopts
136         return self.addNode( name, **opts )
137
138     def addSwitch( self, name, **opts ):
139         """Convenience method: Add switch to graph.
140            name: switch name
141            opts: switch options
142            returns: switch name"""
143         if not opts and self.sopts:
144             opts = self.sopts
145         result = self.addNode( name, isSwitch=True, **opts )
146         return result
147
148     def addLink( self, node1, node2, port1=None, port2=None,
149                  key=None, **opts ):
150         """node1, node2: nodes to link together
151            port1, port2: ports (optional)
152            opts: link options (optional)
153            returns: link info key"""
154         if not opts and self.lopts:
155             opts = self.lopts
156         port1, port2 = self.addPort( node1, node2, port1, port2 )
157         opts = dict( opts )
158         opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
159         self.g.add_edge(node1, node2, key, opts )
160         return key
161
162     def nodes( self, sort=True ):
163         "Return nodes in graph"
164         if sort:
165             return self.sorted( self.g.nodes() )
166         else:
167             return self.g.nodes()
168
169     def isSwitch( self, n ):
170         "Returns true if node is a switch."
171         return self.g.node[ n ].get( 'isSwitch', False )
172
173     def switches( self, sort=True ):
174         """Return switches.
175            sort: sort switches alphabetically
176            returns: dpids list of dpids"""
177         return [ n for n in self.nodes( sort ) if self.isSwitch( n ) ]
178
179     def hosts( self, sort=True ):
180         """Return hosts.
181            sort: sort hosts alphabetically
182            returns: list of hosts"""
183         return [ n for n in self.nodes( sort ) if not self.isSwitch( n ) ]
184
185     def iterLinks( self, withKeys=False, withInfo=False ):
186         """Return links (iterator)
187            withKeys: return link keys
188            withInfo: return link info
189            returns: list of ( src, dst [,key, info ] )"""
190         for _src, _dst, key, info in self.g.edges_iter( data=True, keys=True ):
191             node1, node2 = info[ 'node1' ], info[ 'node2' ]
192             if withKeys:
193                 if withInfo:
194                     yield( node1, node2, key, info )
195                 else:
196                     yield( node1, node2, key )
197             else:
198                 if withInfo:
199                     yield( node1, node2, info )
200                 else:
201                     yield( node1, node2 )
202
203     def links( self, sort=False, withKeys=False, withInfo=False ):
204         """Return links
205            sort: sort links alphabetically, preserving (src, dst) order
206            withKeys: return link keys
207            withInfo: return link info
208            returns: list of ( src, dst [,key, info ] )"""
209         links = list( self.iterLinks( withKeys, withInfo ) )
210         if not sort:
211             return links
212         # Ignore info when sorting
213         tupleSize = 3 if withKeys else 2
214         return sorted( links, key=( lambda l: naturalSeq( l[ :tupleSize ] ) ) )
215
216     # This legacy port management mechanism is clunky and will probably
217     # be removed at some point.
218
219     def addPort( self, src, dst, sport=None, dport=None ):
220         """Generate port mapping for new edge.
221             src: source switch name
222             dst: destination switch name"""
223         # Initialize if necessary
224         ports = self.ports
225         ports.setdefault( src, {} )
226         ports.setdefault( dst, {} )
227         # New port: number of outlinks + base
228         if sport is None:
229             src_base = 1 if self.isSwitch( src ) else 0
230             sport = len( ports[ src ] ) + src_base
231         if dport is None:
232             dst_base = 1 if self.isSwitch( dst ) else 0
233             dport = len( ports[ dst ] ) + dst_base
234         ports[ src ][ sport ] = ( dst, dport )
235         ports[ dst ][ dport ] = ( src, sport )
236         return sport, dport
237
238     def port( self, src, dst ):
239         """Get port numbers.
240             src: source switch name
241             dst: destination switch name
242             sport: optional source port (otherwise use lowest src port)
243             returns: tuple (sport, dport), where
244                 sport = port on source switch leading to the destination switch
245                 dport = port on destination switch leading to the source switch
246             Note that you can also look up ports using linkInfo()"""
247         # A bit ugly and slow vs. single-link implementation ;-(
248         ports = [ ( sport, entry[ 1 ] )
249                   for sport, entry in self.ports[ src ].items()
250                   if entry[ 0 ] == dst ]
251         return ports if len( ports ) != 1 else ports[ 0 ]
252
253     def _linkEntry( self, src, dst, key=None ):
254         "Helper function: return link entry and key"
255         entry = self.g[ src ][ dst ]
256         if key is None:
257             key = min( entry )
258         return entry, key
259
260     def linkInfo( self, src, dst, key=None ):
261         "Return link metadata dict"
262         entry, key = self._linkEntry( src, dst, key )
263         return entry[ key ]
264
265     def setlinkInfo( self, src, dst, info, key=None ):
266         "Set link metadata dict"
267         entry, key = self._linkEntry( src, dst, key )
268         entry[ key ] = info
269
270     def nodeInfo( self, name ):
271         "Return metadata (dict) for node"
272         return self.g.node[ name ]
273
274     def setNodeInfo( self, name, info ):
275         "Set metadata (dict) for node"
276         self.g.node[ name ] = info
277
278     def convertTo( self, cls, data=True, keys=True ):
279         """Convert to a new object of networkx.MultiGraph-like class cls
280            data: include node and edge data (default True)
281            keys: include edge keys as well as edge data (default True)"""
282         return self.g.convertTo( cls, data=data, keys=keys )
283
284     @staticmethod
285     def sorted( items ):
286         "Items sorted in natural (i.e. alphabetical) order"
287         return sorted( items, key=natural )
288
289
290 # Our idiom defines additional parameters in build(param...)
291 # pylint: disable=arguments-differ
292
293 class SingleSwitchTopo( Topo ):
294     "Single switch connected to k hosts."
295
296     def build( self, k=2, **_opts ):
297         "k: number of hosts"
298         self.k = k
299         switch = self.addSwitch( 's1' )
300         for h in irange( 1, k ):
301             host = self.addHost( 'h%s' % h )
302             self.addLink( host, switch )
303
304
305 class SingleSwitchReversedTopo( Topo ):
306     """Single switch connected to k hosts, with reversed ports.
307        The lowest-numbered host is connected to the highest-numbered port.
308        Useful to verify that Mininet properly handles custom port
309        numberings."""
310
311     def build( self, k=2 ):
312         "k: number of hosts"
313         self.k = k
314         switch = self.addSwitch( 's1' )
315         for h in irange( 1, k ):
316             host = self.addHost( 'h%s' % h )
317             self.addLink( host, switch,
318                           port1=0, port2=( k - h + 1 ) )
319
320
321 class MinimalTopo( SingleSwitchTopo ):
322     "Minimal topology with two hosts and one switch"
323     def build( self ):
324         return SingleSwitchTopo.build( self, k=2 )
325
326
327 class LinearTopo( Topo ):
328     "Linear topology of k switches, with n hosts per switch."
329
330     def build( self, k=2, n=1, **_opts):
331         """k: number of switches
332            n: number of hosts per switch"""
333         self.k = k
334         self.n = n
335
336         if n == 1:
337             genHostName = lambda i, j: 'h%s' % i
338         else:
339             genHostName = lambda i, j: 'h%ss%d' % ( j, i )
340
341         lastSwitch = None
342         for i in irange( 1, k ):
343             # Add switch
344             switch = self.addSwitch( 's%s' % i )
345             # Add hosts to switch
346             for j in irange( 1, n ):
347                 host = self.addHost( genHostName( i, j ) )
348                 self.addLink( host, switch )
349             # Connect switch to previous
350             if lastSwitch:
351                 self.addLink( switch, lastSwitch )
352             lastSwitch = switch
353
354 # pylint: enable=arguments-differ