Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 3f2355a3

History | View | Annotate | Download (8.8 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
from mininet.node import NAT
16

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

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

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

    
27
    def add_edge( self, src, dest ):
28
        "Add edge to graph"
29
        src, dest = sorted( ( src, dest ) )
30
        self.add_node( src )
31
        self.add_node( dest )
32
        self.data[ src ].append( dest )
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 = MultiGraph()
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 addNAT(self, name='nat', connect=True, inNamespace=False, **opts):
94
        """Convenience method: Add NAT to graph.
95
           name: NAT name
96
           connect: True will automatically connect to the first switch"""
97
        #nat = self.addNode(name, isNAT=True, inNamespace=False)
98
        nat = self.addNode(name, cls=NAT, inNamespace=inNamespace, hosts=self.hosts(), **opts)
99
        if connect:
100
            # connect the NAT to the first switch
101
            self.addLink(name, self.switches()[ 0 ])
102
        return nat
103

    
104
    def addLink(self, node1, node2, port1=None, port2=None,
105
                **opts):
106
        """node1, node2: nodes to link together
107
           port1, port2: ports (optional)
108
           opts: link options (optional)
109
           returns: link info key"""
110
        if not opts and self.lopts:
111
            opts = self.lopts
112
        self.addPort(node1, node2, port1, port2)
113
        key = tuple(self.sorted([node1, node2]))
114
        self.link_info[key] = opts
115
        self.g.add_edge(*key)
116
        return key
117

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

    
135
    def nodes(self, sort=True):
136
        "Return nodes in graph"
137
        if sort:
138
            return self.sorted( self.g.nodes() )
139
        else:
140
            return self.g.nodes()
141

    
142
    def isSwitch(self, n):
143
        '''Returns true if node is a switch.'''
144
        info = self.node_info[n]
145
        return info and info.get('isSwitch', False)
146

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

    
154
    def hosts(self, sort=True):
155
        '''Return hosts.
156
        sort: sort hosts alphabetically
157
        @return dpids list of dpids
158
        '''
159
        return [n for n in self.nodes(sort) if not self.isSwitch(n)]
160

    
161
    def links(self, sort=True):
162
        '''Return links.
163
        sort: sort links alphabetically
164
        @return links list of name pairs
165
        '''
166
        if not sort:
167
            return self.g.edges()
168
        else:
169
            links = [tuple(self.sorted(e)) for e in self.g.edges()]
170
            return sorted( links, key=naturalSeq )
171

    
172
    def port(self, src, dst):
173
        '''Get port number.
174

175
        @param src source switch name
176
        @param dst destination switch name
177
        @return tuple (src_port, dst_port):
178
            src_port: port on source switch leading to the destination switch
179
            dst_port: port on destination switch leading to the source switch
180
        '''
181
        if src in self.ports and dst in self.ports[src]:
182
            assert dst in self.ports and src in self.ports[dst]
183
            return (self.ports[src][dst], self.ports[dst][src])
184

    
185
    def linkInfo( self, src, dst ):
186
        "Return link metadata"
187
        src, dst = self.sorted([src, dst])
188
        return self.link_info[(src, dst)]
189

    
190
    def setlinkInfo( self, src, dst, info ):
191
        "Set link metadata"
192
        src, dst = self.sorted([src, dst])
193
        self.link_info[(src, dst)] = info
194

    
195
    def nodeInfo( self, name ):
196
        "Return metadata (dict) for node"
197
        info = self.node_info[ name ]
198
        return info if info is not None else {}
199

    
200
    def setNodeInfo( self, name, info ):
201
        "Set metadata (dict) for node"
202
        self.node_info[ name ] = info
203

    
204
    @staticmethod
205
    def sorted( items ):
206
        "Items sorted in natural (i.e. alphabetical) order"
207
        return sorted(items, key=natural)
208

    
209
class SingleSwitchTopo(Topo):
210
    '''Single switch connected to k hosts.'''
211

    
212
    def __init__(self, k=2, **opts):
213
        '''Init.
214

215
        @param k number of hosts
216
        @param enable_all enables all nodes and switches?
217
        '''
218
        super(SingleSwitchTopo, self).__init__(**opts)
219

    
220
        self.k = k
221

    
222
        switch = self.addSwitch('s1')
223
        for h in irange(1, k):
224
            host = self.addHost('h%s' % h)
225
            self.addLink(host, switch)
226

    
227

    
228
class SingleSwitchReversedTopo(Topo):
229
    '''Single switch connected to k hosts, with reversed ports.
230

231
    The lowest-numbered host is connected to the highest-numbered port.
232

233
    Useful to verify that Mininet properly handles custom port numberings.
234
    '''
235
    def __init__(self, k=2, **opts):
236
        '''Init.
237

238
        @param k number of hosts
239
        @param enable_all enables all nodes and switches?
240
        '''
241
        super(SingleSwitchReversedTopo, self).__init__(**opts)
242
        self.k = k
243
        switch = self.addSwitch('s1')
244
        for h in irange(1, k):
245
            host = self.addHost('h%s' % h)
246
            self.addLink(host, switch,
247
                         port1=0, port2=(k - h + 1))
248

    
249
class LinearTopo(Topo):
250
    "Linear topology of k switches, with n hosts per switch."
251

    
252
    def __init__(self, k=2, n=1, **opts):
253
        """Init.
254
           k: number of switches
255
           n: number of hosts per switch
256
           hconf: host configuration options
257
           lconf: link configuration options"""
258

    
259
        super(LinearTopo, self).__init__(**opts)
260

    
261
        self.k = k
262
        self.n = n
263

    
264
        if n == 1:
265
            genHostName = lambda i, j: 'h%s' % i
266
        else:
267
            genHostName = lambda i, j: 'h%ss%d' % (j, i)
268

    
269

    
270
        lastSwitch = None
271
        for i in irange(1, k):
272
            # Add switch
273
            switch = self.addSwitch('s%s' % i)
274
            # Add hosts to switch
275
            for j in irange(1, n):
276
                host = self.addHost(genHostName(i, j))
277
                self.addLink(host, switch)
278
            # Connect switch to previous
279
            if lastSwitch:
280
                self.addLink(switch, lastSwitch)
281
            lastSwitch = switch