Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ c273f490

History | View | Annotate | Download (11.9 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 2485d57f Bob Lantz
82 4e1630e1 Brandon Heller
    def __getitem__( self, node ):
83 89fb0819 Bob Lantz
        "Return link dict for given src node"
84
        return self.edge[ node ]
85
86
    def __len__( self ):
87
        "Return the number of nodes"
88
        return len( self.node )
89
90 634761b8 Bob Lantz
    def convertTo( self, cls, data=False, keys=False ):
91 89fb0819 Bob Lantz
        """Convert to a new object of networkx.MultiGraph-like class cls
92 634761b8 Bob Lantz
           data: include node and edge data
93
           keys: include edge keys as well as edge data"""
94 89fb0819 Bob Lantz
        g = cls()
95
        g.add_nodes_from( self.nodes( data=data ) )
96 634761b8 Bob Lantz
        g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
97 89fb0819 Bob Lantz
        return g
98 4e1630e1 Brandon Heller
99 65c35b65 ryanc
100 38ce329e Bob Lantz
class Topo( object ):
101 5a8bb489 Bob Lantz
    "Data center network representation for structured multi-trees."
102
103 89fb0819 Bob Lantz
    def __init__( self, *args, **params ):
104 1324ae62 Bob Lantz
        """Topo object. 
105
           Optional named parameters:
106 5a8bb489 Bob Lantz
           hinfo: default host options
107
           sopts: default switch options
108 1324ae62 Bob Lantz
           lopts: default link options
109
           calls build()"""
110 5b48a7d9 Bob Lantz
        self.g = MultiGraph()
111 1324ae62 Bob Lantz
        self.hopts = params.pop( 'hopts', {} )
112
        self.sopts = params.pop( 'sopts', {} )
113
        self.lopts = params.pop( 'lopts', {} )
114 89fb0819 Bob Lantz
        self.ports = {}  # ports[src][dst][sport] is port on dst that connects to src
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
                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
        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 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 8dea57d2 Bob Lantz
        if not sorted:
211
            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
    
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 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 1b2c7a31 Bob Lantz
class SingleSwitchTopo( Topo ):
291
    "Single switch connected to k hosts."
292 433a7cc8 Brandon Heller
293 1b2c7a31 Bob Lantz
    def build( self, k=2, **opts ):
294
        "k: number of hosts"
295 433a7cc8 Brandon Heller
        self.k = k
296 1b2c7a31 Bob Lantz
        switch = self.addSwitch( 's1' )
297
        for h in irange( 1, k ):
298
            host = self.addHost( 'h%s' % h )
299
            self.addLink( host, switch )
300 433a7cc8 Brandon Heller
301 6d2cd77b Brandon Heller
302 1b2c7a31 Bob Lantz
class SingleSwitchReversedTopo( Topo ):
303
    """Single switch connected to k hosts, with reversed ports.
304
       The lowest-numbered host is connected to the highest-numbered port.
305
       Useful to verify that Mininet properly handles custom port numberings."""
306 6d2cd77b Brandon Heller
307 1b2c7a31 Bob Lantz
    def build( self, k=2 ):
308
        "k: number of hosts"
309 e1246c37 Bob Lantz
        self.k = k
310 1b2c7a31 Bob Lantz
        switch = self.addSwitch( 's1' )
311
        for h in irange( 1, k ):
312
            host = self.addHost( 'h%s' % h )
313
            self.addLink( host, switch,
314
                          port1=0, port2=( k - h + 1 ) )
315 6d2cd77b Brandon Heller
316 89fb0819 Bob Lantz
317 1b2c7a31 Bob Lantz
class LinearTopo( Topo ):
318 92112315 Brian O'Connor
    "Linear topology of k switches, with n hosts per switch."
319 433a7cc8 Brandon Heller
320 1b2c7a31 Bob Lantz
    def build( self, k=2, n=1, **opts):
321
        """k: number of switches
322
           n: number of hosts per switch"""
323 433a7cc8 Brandon Heller
        self.k = k
324 92112315 Brian O'Connor
        self.n = n
325 433a7cc8 Brandon Heller
326 bb0006b6 Brian O'Connor
        if n == 1:
327 f796f01f Bob Lantz
            genHostName = lambda i, j: 'h%s' % i
328 a22e2618 Murphy McCauley
        else:
329 1b2c7a31 Bob Lantz
            genHostName = lambda i, j: 'h%ss%d' % ( j, i )
330 433a7cc8 Brandon Heller
331 5a8bb489 Bob Lantz
        lastSwitch = None
332 1b2c7a31 Bob Lantz
        for i in irange( 1, k ):
333 92112315 Brian O'Connor
            # Add switch
334 1b2c7a31 Bob Lantz
            switch = self.addSwitch( 's%s' % i )
335 92112315 Brian O'Connor
            # Add hosts to switch
336 1b2c7a31 Bob Lantz
            for j in irange( 1, n ):
337
                host = self.addHost( genHostName( i, j ) )
338
                self.addLink( host, switch )
339 92112315 Brian O'Connor
            # Connect switch to previous
340 5a8bb489 Bob Lantz
            if lastSwitch:
341 1b2c7a31 Bob Lantz
                self.addLink( switch, lastSwitch )
342 5a8bb489 Bob Lantz
            lastSwitch = switch