Revision 89fb0819

View differences:

mininet/net.py
321 321
        "return (key,value) tuple list for every node in net"
322 322
        return zip( self.keys(), self.values() )
323 323

  
324
    @staticmethod
325
    def randMac():
326
        "Return a random, non-multicast MAC address"
327
        return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff  | 0x020000000000 )
328
    
324 329
    def addLink( self, node1, node2, port1=None, port2=None,
325
                 cls=None, **params ):
330
                 cls=None, paramDict=None, **params ):
326 331
        """"Add a link from node1 to node2
327 332
            node1: source node
328 333
            node2: dest node
329
            port1: source port
330
            port2: dest port
334
            port1: source port (optional)
335
            port2: dest port (optional)
336
            cls: link class (optional)
337
            paramDict: dictionary of additional link params (optional)
338
            params: additional link params (optional)
331 339
            returns: link object"""
332
        mac1 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff  | 0x020000000000 )
333
        mac2 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff  | 0x020000000000 )
334
        defaults = { 'port1': port1,
335
                     'port2': port2,
336
                     'addr1': mac1,
337
                     'addr2': mac2,
338
                     'intf': self.intf }
339
        defaults.update( params )
340
        if not cls:
341
            cls = self.link
342
        link = cls( node1, node2, **defaults )
340
        mac1 = self.randMac()
341
        mac2 = self.randMac()
342
        paramDict = {} if paramDict is None else paramDict
343
        paramDict.update( params )
344
        # Ugly: try to ensure that node1 and node2 line up correctly with
345
        # other link parameters
346
        node1 = self[ paramDict.pop( 'node1', node1.name ) ]
347
        node2 = self[ paramDict.pop( 'node2', node2.name ) ]
348
        paramDict.setdefault( 'port1', port1 )
349
        paramDict.setdefault( 'port2', port2 )
350
        paramDict.setdefault( 'addr1', mac1 )
351
        paramDict.setdefault( 'addr2', mac2 )
352
        cls = self.link if cls is None else cls
353
        link = cls( node1, node2, **paramDict )
343 354
        self.links.append( link )
344 355
        return link
345 356

  
......
397 408
            info( switchName + ' ' )
398 409

  
399 410
        info( '\n*** Adding links:\n' )
400
        for srcName, dstName in topo.links(sort=True):
411
        for srcName, dstName in topo.links( sort=True ):
401 412
            src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
402 413
            params = topo.linkInfo( srcName, dstName )
403 414
            srcPort, dstPort = topo.port( srcName, dstName )
404
            self.addLink( src, dst, srcPort, dstPort, **params )
415
            self.addLink( src, dst, srcPort, dstPort, paramDict=params )
405 416
            info( '(%s, %s) ' % ( src.name, dst.name ) )
406 417

  
407 418
        info( '\n' )
mininet/topo.py
1 1
#!/usr/bin/env python
2
'''@package topo
2
"""@package topo
3 3

  
4 4
Network topology creation.
5 5

  
......
9 9

  
10 10
A Topo object can be a topology database for NOX, can represent a physical
11 11
setup for testing, and can even be emulated with the Mininet package.
12
'''
12
"""
13 13

  
14 14
from mininet.util import irange, natural, naturalSeq
15 15

  
16 16
class MultiGraph( object ):
17
    "Utility class to track nodes and edges - replaces networkx.Graph"
17
    "Utility class to track nodes and edges - replaces networkx.MultiGraph"
18 18

  
19 19
    def __init__( self ):
20
        self.data = {}
21

  
22
    def add_node( self, node ):
23
        "Add node to graph"
24
        self.data.setdefault( node, [] )
25

  
26
    def add_edge( self, src, dest ):
27
        "Add edge to graph"
28
        src, dest = sorted( ( src, dest ) )
29
        self.add_node( src )
30
        self.add_node( dest )
31
        self.data[ src ].append( dest )
20
        self.node = {}
21
        self.edge = {}
22
        self.edgeinfo = {}
23

  
24
    def add_node( self, node, attr_dict=None, **attrs):
25
        """Add node to graph
26
           attr_dict: attribute dict (optional)
27
           attrs: more attributes (optional)"""
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
        attr_dict = {} if attr_dict is None else attr_dict
37
        attr_dict.update( attrs )
38
        self.node.setdefault( src, {} )
39
        self.node.setdefault( dst, {} )
40
        self.edge.setdefault( src, {} )
41
        self.edge.setdefault( dst, {} )
42
        self.edge[ src ].setdefault( dst, {} )
43
        entry = self.edge[ dst ][ src ] = self.edge[ src ][ dst ]
44
        # If no key, pick next ordinal number
45
        if key is None:
46
            keys = [ k for k in entry.keys() if type( k ) is int ]
47
            key = max( [ 0 ] + keys ) + 1
48
        entry[ key ] = attr_dict
49
        return key
32 50

  
33
    def nodes( self ):
34
        "Return list of graph nodes"
35
        return self.data.keys()
51
    def nodes( self, data=False):
52
        """Return list of graph nodes
53
           data: return list of ( node, attrs)"""
54
        return self.node.items() if data else self.node.keys()
36 55

  
37
    def edges( self ):
56
    def edges_iter( self, data=False ):
38 57
        "Iterator: return graph edges"
39
        for src in self.data.keys():
40
            for dest in self.data[ src ]:
41
                yield ( src, dest )
58
        for src, entry in self.edge.iteritems():
59
            for dst, keys in entry.iteritems():
60
                if [ src, dst ] != sorted( [ src, dst ] ):
61
                    # Skip duplicate edges
62
                    continue
63
                for key, attrs in keys.iteritems():
64
                    if data:
65
                        yield( ( src, dst, attrs ) )
66
                    else:
67
                        yield( ( src, dst ) )
68

  
69
    def edges( self, data=False ):
70
        "Return list of graph edges"
71
        return list( self.edges_iter( data ) )
72

  
42 73

  
43 74
    def __getitem__( self, node ):
44
        "Return link dict for the given node"
45
        return self.data[node]
75
        "Return link dict for given src node"
76
        return self.edge[ node ]
77

  
78
    def __len__( self ):
79
        "Return the number of nodes"
80
        return len( self.node )
81

  
82
    def convertTo( self, cls, data=False ):
83
        """Convert to a new object of networkx.MultiGraph-like class cls
84
           data: include node and edge data"""
85
        g = cls()
86
        g.add_nodes_from( self.nodes( data=data ) )
87
        g.add_edges_from( self.edges( data=data ) )
88
        return g
46 89

  
47 90

  
48 91
class Topo(object):
49 92
    "Data center network representation for structured multi-trees."
50 93

  
51
    def __init__(self, *args, **params):
94
    def __init__( self, *args, **params ):
52 95
        """Topo object. 
53 96
           Optional named parameters:
54 97
           hinfo: default host options
......
56 99
           lopts: default link options
57 100
           calls build()"""
58 101
        self.g = MultiGraph()
59
        self.node_info = {}
60
        self.link_info = {}  # (src, dst) tuples hash to EdgeInfo objects
61 102
        self.hopts = params.pop( 'hopts', {} )
62 103
        self.sopts = params.pop( 'sopts', {} )
63 104
        self.lopts = params.pop( 'lopts', {} )
64
        self.ports = {}  # ports[src][dst] is port on src that connects to dst
105
        self.ports = {}  # ports[src][dst][sport] is port on dst that connects to src
65 106
        self.build( *args, **params )
66 107

  
67 108
    def build( self, *args, **params ):
68 109
        "Override this method to build your topology."
69 110
        pass
70 111

  
71
    def addNode(self, name, **opts):
112
    def addNode( self, name, **opts ):
72 113
        """Add Node to graph.
73 114
           name: name
74 115
           opts: node options
75 116
           returns: node name"""
76
        self.g.add_node(name)
77
        self.node_info[name] = opts
117
        self.g.add_node( name, **opts )
78 118
        return name
79 119

  
80
    def addHost(self, name, **opts):
120
    def addHost( self, name, **opts ):
81 121
        """Convenience method: Add host to graph.
82 122
           name: host name
83 123
           opts: host options
......
86 126
            opts = self.hopts
87 127
        return self.addNode(name, **opts)
88 128

  
89
    def addSwitch(self, name, **opts):
129
    def addSwitch( self, name, **opts ):
90 130
        """Convenience method: Add switch to graph.
91 131
           name: switch name
92 132
           opts: switch options
......
96 136
        result = self.addNode(name, isSwitch=True, **opts)
97 137
        return result
98 138

  
99
    def addLink(self, node1, node2, port1=None, port2=None,
100
                **opts):
139
    def addLink( self, node1, node2, port1=None, port2=None,
140
                key=None, **opts ):
101 141
        """node1, node2: nodes to link together
102 142
           port1, port2: ports (optional)
103 143
           opts: link options (optional)
104 144
           returns: link info key"""
105 145
        if not opts and self.lopts:
106 146
            opts = self.lopts
107
        self.addPort(node1, node2, port1, port2)
108
        key = tuple(self.sorted([node1, node2]))
109
        self.link_info[key] = opts
110
        self.g.add_edge(*key)
147
        port1, port2 = self.addPort(node1, node2, port1, port2)
148
        opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
149
        self.g.add_edge(node1, node2, key, opts )
111 150
        return key
112 151

  
113
    def addPort(self, src, dst, sport=None, dport=None):
114
        '''Generate port mapping for new edge.
115
        @param src source switch name
116
        @param dst destination switch name
117
        '''
118
        self.ports.setdefault(src, {})
119
        self.ports.setdefault(dst, {})
120
        # New port: number of outlinks + base
121
        src_base = 1 if self.isSwitch(src) else 0
122
        dst_base = 1 if self.isSwitch(dst) else 0
123
        if sport is None:
124
            sport = len(self.ports[src]) + src_base
125
        if dport is None:
126
            dport = len(self.ports[dst]) + dst_base
127
        self.ports[src][dst] = sport
128
        self.ports[dst][src] = dport
129

  
130
    def nodes(self, sort=True):
152
    def nodes( self, sort=True ):
131 153
        "Return nodes in graph"
132 154
        if sort:
133 155
            return self.sorted( self.g.nodes() )
134 156
        else:
135 157
            return self.g.nodes()
136 158

  
137
    def isSwitch(self, n):
138
        '''Returns true if node is a switch.'''
139
        info = self.node_info[n]
140
        return info and info.get('isSwitch', False)
141

  
142
    def switches(self, sort=True):
143
        '''Return switches.
144
        sort: sort switches alphabetically
145
        @return dpids list of dpids
146
        '''
147
        return [n for n in self.nodes(sort) if self.isSwitch(n)]
148

  
149
    def hosts(self, sort=True):
150
        '''Return hosts.
151
        sort: sort hosts alphabetically
152
        @return dpids list of dpids
153
        '''
154
        return [n for n in self.nodes(sort) if not self.isSwitch(n)]
155

  
156
    def links(self, sort=True):
157
        '''Return links.
158
        sort: sort links alphabetically
159
        @return links list of name pairs
160
        '''
159
    def isSwitch( self, n ):
160
        "Returns true if node is a switch."
161
        return self.g.node[ n ].get( 'isSwitch', False )
162

  
163
    def switches( self, sort=True ):
164
        """Return switches.
165
           sort: sort switches alphabetically
166
           returns: dpids list of dpids"""
167
        return [ n for n in self.nodes( sort ) if self.isSwitch( n ) ]
168

  
169
    def hosts( self, sort=True ):
170
        """Return hosts.
171
           sort: sort hosts alphabetically
172
           returns: list of hosts"""
173
        return [ n for n in self.nodes( sort ) if not self.isSwitch( n ) ]
174

  
175
    def links( self, sort=True, withKeys=False ):
176
        """Return links.
177
           sort: sort links alphabetically
178
           withKeys: return key in tuple
179
           @return links list of ( src, dst [,key ] )"""
161 180
        if not sort:
162
            return self.g.edges()
181
            return self.g.edges( withKeys )
163 182
        else:
164
            links = [tuple(self.sorted(e)) for e in self.g.edges()]
183
            if withKeys:
184
                links = [ tuple( self.sorted( ( s, d ) ) ) + [ k ]
185
                          for s, d, k in self.g.edges( data=True ) ]
186
            else:
187
                links = [ tuple ( self.sorted( e ) ) for e in self.g.edges() ]
165 188
            return sorted( links, key=naturalSeq )
166 189

  
167
    def port(self, src, dst):
168
        '''Get port number.
169

  
170
        @param src source switch name
171
        @param dst destination switch name
172
        @return tuple (src_port, dst_port):
173
            src_port: port on source switch leading to the destination switch
174
            dst_port: port on destination switch leading to the source switch
175
        '''
176
        if src in self.ports and dst in self.ports[src]:
177
            assert dst in self.ports and src in self.ports[dst]
178
            return self.ports[src][dst], self.ports[dst][src]
179

  
180
    def linkInfo( self, src, dst ):
181
        "Return link metadata"
182
        src, dst = self.sorted([src, dst])
183
        return self.link_info[(src, dst)]
184

  
185
    def setlinkInfo( self, src, dst, info ):
186
        "Set link metadata"
187
        src, dst = self.sorted([src, dst])
188
        self.link_info[(src, dst)] = info
190
    # This legacy port management mechanism is clunky and will probably
191
    # be removed at some point.
192

  
193
    def addPort( self, src, dst, sport=None, dport=None ):
194
        """Generate port mapping for new edge.
195
            src: source switch name
196
            dst: destination switch name"""
197
        # Initialize if necessary
198
        ports = self.ports
199
        ports.setdefault( src, {} )
200
        ports.setdefault( dst, {} )
201
        # New port: number of outlinks + base
202
        if sport is None:
203
            src_base = 1 if self.isSwitch( src ) else 0
204
            sport = len( ports[ src ] ) + src_base
205
        if dport is None:
206
            dst_base = 1 if self.isSwitch( dst ) else 0
207
            dport = len( ports[ dst ] ) + dst_base
208
        ports[ src ][ sport ] = ( dst, dport )
209
        ports[ dst ][ dport ] = ( src, sport )
210
        return sport, dport
211

  
212
    def port( self, src, dst ):
213
        """Get port numbers.
214
            src: source switch name
215
            dst: destination switch name
216
            sport: optional source port (otherwise use lowest src port)
217
            returns: tuple (sport, dport), where
218
                sport = port on source switch leading to the destination switch
219
                dport = port on destination switch leading to the source switch
220
            Note that you can also look up ports using linkInfo()"""
221
        # A bit ugly and slow vs. single-link implementation ;-(
222
        ports = [ ( sport, entry[ 1 ] )
223
                  for sport, entry in self.ports[ src ].items()
224
                  if entry[ 0 ] == dst ]
225
        return ports if len( ports ) != 1 else ports[ 0 ]
226

  
227
    def _linkEntry( self, src, dst, key=None ):
228
        "Helper function: return link entry and key"
229
        entry = self.g[ src ][ dst ]
230
        if key is None:
231
            key = min( entry )
232
        return entry, key
233
    
234
    def linkInfo( self, src, dst, key=None ):
235
        "Return link metadata dict"
236
        entry, key = self._linkEntry( src, dst, key )
237
        return entry[ key ]
238

  
239
    def setlinkInfo( self, src, dst, info, key=None ):
240
        "Set link metadata dict"
241
        entry, key = self._linkEntry( src, dst, key )
242
        entry [ key ] = info
189 243

  
190 244
    def nodeInfo( self, name ):
191 245
        "Return metadata (dict) for node"
192
        info = self.node_info[ name ]
193
        return info if info is not None else {}
246
        return self.g.node[ name ]
194 247

  
195 248
    def setNodeInfo( self, name, info ):
196 249
        "Set metadata (dict) for node"
197
        self.node_info[ name ] = info
250
        self.g.node[ name ] = info
198 251

  
199 252
    @staticmethod
200 253
    def sorted( items ):
201 254
        "Items sorted in natural (i.e. alphabetical) order"
202
        return sorted(items, key=natural)
255
        return sorted( items, key=natural )
203 256

  
204 257

  
205 258
class SingleSwitchTopo( Topo ):
......
228 281
            self.addLink( host, switch,
229 282
                          port1=0, port2=( k - h + 1 ) )
230 283

  
284

  
231 285
class LinearTopo( Topo ):
232 286
    "Linear topology of k switches, with n hosts per switch."
233 287

  

Also available in: Unified diff