Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ e5d7b380

History | View | Annotate | Download (7.94 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 Graph( 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
        if node not in self.data.keys():
25
            self.data[ node ] = []
26

    
27
    def add_edge( self, src, dest ):
28
        "Add edge to graph"
29
        self.add_node( src )
30
        self.add_node( dest )
31
        self.data[ src ].append( dest )
32
        self.data[ dest ].append( src )
33

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

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

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

    
48

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
209
        self.k = k
210

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

    
216

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

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

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

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

    
238
class LinearTopo(Topo):
239
    "Linear topology of k switches, with one host per switch."
240

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

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

    
249
        self.k = k
250

    
251
        lastSwitch = None
252
        for i in irange(1, k):
253
            host = self.addHost('h%s' % i)
254
            switch = self.addSwitch('s%s' % i)
255
            self.addLink( host, switch)
256
            if lastSwitch:
257
                self.addLink( switch, lastSwitch)
258
            lastSwitch = switch