Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 824afb84

History | View | Annotate | Download (8.25 KB)

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.Graph"
18

    
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 )
32

    
33
    def nodes( self ):
34
        "Return list of graph nodes"
35
        return self.data.keys()
36

    
37
    def edges( self ):
38
        "Iterator: return graph edges"
39
        for src in self.data.keys():
40
            for dest in self.data[ src ]:
41
                yield ( src, dest )
42

    
43
    def __getitem__( self, node ):
44
        "Return link dict for the given node"
45
        return self.data[node]
46

    
47

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

    
51
    def __init__(self, hopts=None, sopts=None, lopts=None):
52
        """Topo object:
53
           hinfo: default host options
54
           sopts: default switch options
55
           lopts: default link options"""
56
        self.g = MultiGraph()
57
        self.node_info = {}
58
        self.link_info = {}  # (src, dst) tuples hash to EdgeInfo objects
59
        self.hopts = {} if hopts is None else hopts
60
        self.sopts = {} if sopts is None else sopts
61
        self.lopts = {} if lopts is None else lopts
62
        self.ports = {}  # ports[src][dst] is port on src that connects to dst
63

    
64
    def addNode(self, name, **opts):
65
        """Add Node to graph.
66
           name: name
67
           opts: node options
68
           returns: node name"""
69
        self.g.add_node(name)
70
        self.node_info[name] = opts
71
        return name
72

    
73
    def addHost(self, name, **opts):
74
        """Convenience method: Add host to graph.
75
           name: host name
76
           opts: host options
77
           returns: host name"""
78
        if not opts and self.hopts:
79
            opts = self.hopts
80
        return self.addNode(name, **opts)
81

    
82
    def addSwitch(self, name, **opts):
83
        """Convenience method: Add switch to graph.
84
           name: switch name
85
           opts: switch options
86
           returns: switch name"""
87
        if not opts and self.sopts:
88
            opts = self.sopts
89
        result = self.addNode(name, isSwitch=True, **opts)
90
        return result
91

    
92
    def addLink(self, node1, node2, port1=None, port2=None,
93
                **opts):
94
        """node1, node2: nodes to link together
95
           port1, port2: ports (optional)
96
           opts: link options (optional)
97
           returns: link info key"""
98
        if not opts and self.lopts:
99
            opts = self.lopts
100
        self.addPort(node1, node2, port1, port2)
101
        key = tuple(self.sorted([node1, node2]))
102
        self.link_info[key] = opts
103
        self.g.add_edge(*key)
104
        return key
105

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

    
123
    def nodes(self, sort=True):
124
        "Return nodes in graph"
125
        if sort:
126
            return self.sorted( self.g.nodes() )
127
        else:
128
            return self.g.nodes()
129

    
130
    def isSwitch(self, n):
131
        '''Returns true if node is a switch.'''
132
        info = self.node_info[n]
133
        return info and info.get('isSwitch', False)
134

    
135
    def switches(self, sort=True):
136
        '''Return switches.
137
        sort: sort switches alphabetically
138
        @return dpids list of dpids
139
        '''
140
        return [n for n in self.nodes(sort) if self.isSwitch(n)]
141

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

    
149
    def links(self, sort=True):
150
        '''Return links.
151
        sort: sort links alphabetically
152
        @return links list of name pairs
153
        '''
154
        if not sort:
155
            return self.g.edges()
156
        else:
157
            links = [tuple(self.sorted(e)) for e in self.g.edges()]
158
            return sorted( links, key=naturalSeq )
159

    
160
    def port(self, src, dst):
161
        '''Get port number.
162

163
        @param src source switch name
164
        @param dst destination switch name
165
        @return tuple (src_port, dst_port):
166
            src_port: port on source switch leading to the destination switch
167
            dst_port: port on destination switch leading to the source switch
168
        '''
169
        if src in self.ports and dst in self.ports[src]:
170
            assert dst in self.ports and src in self.ports[dst]
171
            return self.ports[src][dst], self.ports[dst][src]
172

    
173
    def linkInfo( self, src, dst ):
174
        "Return link metadata"
175
        src, dst = self.sorted([src, dst])
176
        return self.link_info[(src, dst)]
177

    
178
    def setlinkInfo( self, src, dst, info ):
179
        "Set link metadata"
180
        src, dst = self.sorted([src, dst])
181
        self.link_info[(src, dst)] = info
182

    
183
    def nodeInfo( self, name ):
184
        "Return metadata (dict) for node"
185
        info = self.node_info[ name ]
186
        return info if info is not None else {}
187

    
188
    def setNodeInfo( self, name, info ):
189
        "Set metadata (dict) for node"
190
        self.node_info[ name ] = info
191

    
192
    @staticmethod
193
    def sorted( items ):
194
        "Items sorted in natural (i.e. alphabetical) order"
195
        return sorted(items, key=natural)
196

    
197
class SingleSwitchTopo(Topo):
198
    '''Single switch connected to k hosts.'''
199

    
200
    def __init__(self, k=2, **opts):
201
        '''Init.
202

203
        @param k number of hosts
204
        @param enable_all enables all nodes and switches?
205
        '''
206
        super(SingleSwitchTopo, self).__init__(**opts)
207

    
208
        self.k = k
209

    
210
        switch = self.addSwitch('s1')
211
        for h in irange(1, k):
212
            host = self.addHost('h%s' % h)
213
            self.addLink(host, switch)
214

    
215

    
216
class SingleSwitchReversedTopo(Topo):
217
    '''Single switch connected to k hosts, with reversed ports.
218

219
    The lowest-numbered host is connected to the highest-numbered port.
220

221
    Useful to verify that Mininet properly handles custom port numberings.
222
    '''
223
    def __init__(self, k=2, **opts):
224
        '''Init.
225

226
        @param k number of hosts
227
        @param enable_all enables all nodes and switches?
228
        '''
229
        super(SingleSwitchReversedTopo, self).__init__(**opts)
230
        self.k = k
231
        switch = self.addSwitch('s1')
232
        for h in irange(1, k):
233
            host = self.addHost('h%s' % h)
234
            self.addLink(host, switch,
235
                         port1=0, port2=(k - h + 1))
236

    
237
class LinearTopo(Topo):
238
    "Linear topology of k switches, with n hosts per switch."
239

    
240
    def __init__(self, k=2, n=1, **opts):
241
        """Init.
242
           k: number of switches
243
           n: number of hosts per switch
244
           hconf: host configuration options
245
           lconf: link configuration options"""
246

    
247
        super(LinearTopo, self).__init__(**opts)
248

    
249
        self.k = k
250
        self.n = n
251

    
252
        if n == 1:
253
            genHostName = lambda i, j: 'h%s' % i
254
        else:
255
            genHostName = lambda i, j: 'h%ss%d' % (j, i)
256

    
257

    
258
        lastSwitch = None
259
        for i in irange(1, k):
260
            # Add switch
261
            switch = self.addSwitch('s%s' % i)
262
            # Add hosts to switch
263
            for j in irange(1, n):
264
                host = self.addHost(genHostName(i, j))
265
                self.addLink(host, switch)
266
            # Connect switch to previous
267
            if lastSwitch:
268
                self.addLink(switch, lastSwitch)
269
            lastSwitch = switch