Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 7485b035

History | View | Annotate | Download (12.1 KB)

1 8b5062a3 Brandon Heller
#!/usr/bin/env python
2 89fb0819 Bob Lantz
"""@package topo
3 8b5062a3 Brandon Heller

4 433a7cc8 Brandon Heller
Network topology creation.
5 8b5062a3 Brandon Heller

6 433a7cc8 Brandon Heller
@author Brandon Heller (brandonh@stanford.edu)
7 8b5062a3 Brandon Heller

8 433a7cc8 Brandon Heller
This package includes code to represent network topologies.
9 8b5062a3 Brandon Heller

10 433a7cc8 Brandon Heller
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 89fb0819 Bob Lantz
"""
13 8b5062a3 Brandon Heller
14 8bebd377 Bob Lantz
from mininet.util import irange, natural, naturalSeq
15 433a7cc8 Brandon Heller
16 5b48a7d9 Bob Lantz
class MultiGraph( object ):
17 89fb0819 Bob Lantz
    "Utility class to track nodes and edges - replaces networkx.MultiGraph"
18 65c35b65 ryanc
19 2485d57f Bob Lantz
    def __init__( self ):
20 89fb0819 Bob Lantz
        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 3e1100b7 Bob Lantz
           attrs: more attributes (optional)
27
           warning: updates attr_dict with attrs"""
28 89fb0819 Bob Lantz
        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 3e1100b7 Bob Lantz
           attr_dict: optional attribute dict
36
           attrs: more attributes
37
           warning: udpates attr_dict with attrs"""
38 89fb0819 Bob Lantz
        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 c273f490 Bob Lantz
            keys = [ k for k in entry.keys() if isinstance( k, int ) ]
49 89fb0819 Bob Lantz
            key = max( [ 0 ] + keys ) + 1
50
        entry[ key ] = attr_dict
51
        return key
52 65c35b65 ryanc
53 89fb0819 Bob Lantz
    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 65c35b65 ryanc
58 ba8ea8f0 Bob Lantz
    def edges_iter( self, data=False, keys=False ):
59 2485d57f Bob Lantz
        "Iterator: return graph edges"
60 89fb0819 Bob Lantz
        for src, entry in self.edge.iteritems():
61
            for dst, keys in entry.iteritems():
62 ba8ea8f0 Bob Lantz
                if src > dst:
63 89fb0819 Bob Lantz
                    # Skip duplicate edges
64
                    continue
65 38ce329e Bob Lantz
                for k, attrs in keys.iteritems():
66 89fb0819 Bob Lantz
                    if data:
67 ba8ea8f0 Bob Lantz
                        if keys:
68
                            yield( src, dst, k, attrs )
69
                        else:
70
                            yield( src, dst, attrs )
71 89fb0819 Bob Lantz
                    else:
72 ba8ea8f0 Bob Lantz
                        if keys:
73
                            yield( src, dst, k )
74
                        else:
75
                            yield( src, dst )
76 89fb0819 Bob Lantz
77 ba8ea8f0 Bob Lantz
    def edges( self, data=False, keys=False ):
78 89fb0819 Bob Lantz
        "Return list of graph edges"
79 634761b8 Bob Lantz
        return list( self.edges_iter( data=data, keys=keys ) )
80 89fb0819 Bob Lantz
81 4e1630e1 Brandon Heller
    def __getitem__( self, node ):
82 89fb0819 Bob Lantz
        "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 634761b8 Bob Lantz
    def convertTo( self, cls, data=False, keys=False ):
90 89fb0819 Bob Lantz
        """Convert to a new object of networkx.MultiGraph-like class cls
91 634761b8 Bob Lantz
           data: include node and edge data
92
           keys: include edge keys as well as edge data"""
93 89fb0819 Bob Lantz
        g = cls()
94
        g.add_nodes_from( self.nodes( data=data ) )
95 634761b8 Bob Lantz
        g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
96 89fb0819 Bob Lantz
        return g
97 4e1630e1 Brandon Heller
98 65c35b65 ryanc
99 38ce329e Bob Lantz
class Topo( object ):
100 5a8bb489 Bob Lantz
    "Data center network representation for structured multi-trees."
101
102 89fb0819 Bob Lantz
    def __init__( self, *args, **params ):
103 5a530af1 Bob Lantz
        """Topo object.
104 1324ae62 Bob Lantz
           Optional named parameters:
105 5a8bb489 Bob Lantz
           hinfo: default host options
106
           sopts: default switch options
107 1324ae62 Bob Lantz
           lopts: default link options
108
           calls build()"""
109 5b48a7d9 Bob Lantz
        self.g = MultiGraph()
110 1324ae62 Bob Lantz
        self.hopts = params.pop( 'hopts', {} )
111
        self.sopts = params.pop( 'sopts', {} )
112
        self.lopts = params.pop( 'lopts', {} )
113 b1ec912d Bob Lantz
        # ports[src][dst][sport] is port on dst that connects to src
114
        self.ports = {}
115 1324ae62 Bob Lantz
        self.build( *args, **params )
116
117
    def build( self, *args, **params ):
118
        "Override this method to build your topology."
119
        pass
120 433a7cc8 Brandon Heller
121 89fb0819 Bob Lantz
    def addNode( self, name, **opts ):
122 5a8bb489 Bob Lantz
        """Add Node to graph.
123
           name: name
124 e1246c37 Bob Lantz
           opts: node options
125
           returns: node name"""
126 89fb0819 Bob Lantz
        self.g.add_node( name, **opts )
127 5a8bb489 Bob Lantz
        return name
128
129 89fb0819 Bob Lantz
    def addHost( self, name, **opts ):
130 5a8bb489 Bob Lantz
        """Convenience method: Add host to graph.
131 e1246c37 Bob Lantz
           name: host name
132
           opts: host options
133
           returns: host name"""
134 5a8bb489 Bob Lantz
        if not opts and self.hopts:
135
            opts = self.hopts
136 eab4ea3f Bob Lantz
        return self.addNode( name, **opts )
137 5a8bb489 Bob Lantz
138 89fb0819 Bob Lantz
    def addSwitch( self, name, **opts ):
139 5a8bb489 Bob Lantz
        """Convenience method: Add switch to graph.
140 e1246c37 Bob Lantz
           name: switch name
141
           opts: switch options
142
           returns: switch name"""
143 5a8bb489 Bob Lantz
        if not opts and self.sopts:
144
            opts = self.sopts
145 eab4ea3f Bob Lantz
        result = self.addNode( name, isSwitch=True, **opts )
146 5a8bb489 Bob Lantz
        return result
147
148 89fb0819 Bob Lantz
    def addLink( self, node1, node2, port1=None, port2=None,
149 7a3159c9 Bob Lantz
                 key=None, **opts ):
150 e1246c37 Bob Lantz
        """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 eab4ea3f Bob Lantz
        port1, port2 = self.addPort( node1, node2, port1, port2 )
157 3e1100b7 Bob Lantz
        opts = dict( opts )
158 89fb0819 Bob Lantz
        opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
159
        self.g.add_edge(node1, node2, key, opts )
160 e1246c37 Bob Lantz
        return key
161
162 89fb0819 Bob Lantz
    def nodes( self, sort=True ):
163 5a8bb489 Bob Lantz
        "Return nodes in graph"
164
        if sort:
165 e1246c37 Bob Lantz
            return self.sorted( self.g.nodes() )
166 433a7cc8 Brandon Heller
        else:
167 5a8bb489 Bob Lantz
            return self.g.nodes()
168 433a7cc8 Brandon Heller
169 89fb0819 Bob Lantz
    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 ba8ea8f0 Bob Lantz
    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 b1ec912d Bob Lantz
        for _src, _dst, key, info in self.g.edges_iter( data=True, keys=True ):
191 ba8ea8f0 Bob Lantz
            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 89fb0819 Bob Lantz
            else:
198 ba8ea8f0 Bob Lantz
                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 b1ec912d Bob Lantz
        if not sort:
211 8dea57d2 Bob Lantz
            return links
212
        # Ignore info when sorting
213
        tupleSize = 3 if withKeys else 2
214 e77123cf Bob Lantz
        return sorted( links, key=( lambda l: naturalSeq( l[ :tupleSize ] ) ) )
215 433a7cc8 Brandon Heller
216 89fb0819 Bob Lantz
    # 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 de002b0d Bob Lantz
                dport = port on destination switch leading to the source switch
246 89fb0819 Bob Lantz
            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 5a530af1 Bob Lantz
260 89fb0819 Bob Lantz
    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 eab4ea3f Bob Lantz
        entry[ key ] = info
269 8f310286 Bob Lantz
270 5a8bb489 Bob Lantz
    def nodeInfo( self, name ):
271
        "Return metadata (dict) for node"
272 89fb0819 Bob Lantz
        return self.g.node[ name ]
273 433a7cc8 Brandon Heller
274 5a8bb489 Bob Lantz
    def setNodeInfo( self, name, info ):
275 8bebd377 Bob Lantz
        "Set metadata (dict) for node"
276 89fb0819 Bob Lantz
        self.g.node[ name ] = info
277 433a7cc8 Brandon Heller
278 634761b8 Bob Lantz
    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 5a8bb489 Bob Lantz
    @staticmethod
285
    def sorted( items ):
286
        "Items sorted in natural (i.e. alphabetical) order"
287 89fb0819 Bob Lantz
        return sorted( items, key=natural )
288 14ff3ad3 Bob Lantz
289 433a7cc8 Brandon Heller
290 b1ec912d Bob Lantz
# Our idiom defines additional parameters in build(param...)
291 061598f0 Bob Lantz
# pylint: disable=arguments-differ
292 b1ec912d Bob Lantz
293 1b2c7a31 Bob Lantz
class SingleSwitchTopo( Topo ):
294
    "Single switch connected to k hosts."
295 433a7cc8 Brandon Heller
296 b1ec912d Bob Lantz
    def build( self, k=2, **_opts ):
297 1b2c7a31 Bob Lantz
        "k: number of hosts"
298 433a7cc8 Brandon Heller
        self.k = k
299 1b2c7a31 Bob Lantz
        switch = self.addSwitch( 's1' )
300
        for h in irange( 1, k ):
301
            host = self.addHost( 'h%s' % h )
302
            self.addLink( host, switch )
303 433a7cc8 Brandon Heller
304 6d2cd77b Brandon Heller
305 1b2c7a31 Bob Lantz
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 7a3159c9 Bob Lantz
       Useful to verify that Mininet properly handles custom port
309
       numberings."""
310 6d2cd77b Brandon Heller
311 1b2c7a31 Bob Lantz
    def build( self, k=2 ):
312
        "k: number of hosts"
313 e1246c37 Bob Lantz
        self.k = k
314 1b2c7a31 Bob Lantz
        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 6d2cd77b Brandon Heller
320 89fb0819 Bob Lantz
321 1b2c7a31 Bob Lantz
class LinearTopo( Topo ):
322 92112315 Brian O'Connor
    "Linear topology of k switches, with n hosts per switch."
323 433a7cc8 Brandon Heller
324 b1ec912d Bob Lantz
    def build( self, k=2, n=1, **_opts):
325 1b2c7a31 Bob Lantz
        """k: number of switches
326
           n: number of hosts per switch"""
327 433a7cc8 Brandon Heller
        self.k = k
328 92112315 Brian O'Connor
        self.n = n
329 433a7cc8 Brandon Heller
330 bb0006b6 Brian O'Connor
        if n == 1:
331 f796f01f Bob Lantz
            genHostName = lambda i, j: 'h%s' % i
332 a22e2618 Murphy McCauley
        else:
333 1b2c7a31 Bob Lantz
            genHostName = lambda i, j: 'h%ss%d' % ( j, i )
334 433a7cc8 Brandon Heller
335 5a8bb489 Bob Lantz
        lastSwitch = None
336 1b2c7a31 Bob Lantz
        for i in irange( 1, k ):
337 92112315 Brian O'Connor
            # Add switch
338 1b2c7a31 Bob Lantz
            switch = self.addSwitch( 's%s' % i )
339 92112315 Brian O'Connor
            # Add hosts to switch
340 1b2c7a31 Bob Lantz
            for j in irange( 1, n ):
341
                host = self.addHost( genHostName( i, j ) )
342
                self.addLink( host, switch )
343 92112315 Brian O'Connor
            # Connect switch to previous
344 5a8bb489 Bob Lantz
            if lastSwitch:
345 1b2c7a31 Bob Lantz
                self.addLink( switch, lastSwitch )
346 5a8bb489 Bob Lantz
            lastSwitch = switch
347 b1ec912d Bob Lantz
348 061598f0 Bob Lantz
# pylint: enable=arguments-differ