Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 80a8fa62

History | View | Annotate | Download (11 KB)

1 8b5062a3 Brandon Heller
#!/usr/bin/env python
2 433a7cc8 Brandon Heller
'''@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
'''
13 8b5062a3 Brandon Heller
14 723d068c Brandon Heller
from networkx.classes.graph import Graph
15 8b5062a3 Brandon Heller
16
17 433a7cc8 Brandon Heller
class NodeID(object):
18
    '''Topo node identifier.'''
19 8b5062a3 Brandon Heller
20 433a7cc8 Brandon Heller
    def __init__(self, dpid = None):
21
        '''Init.
22

23
        @param dpid dpid
24
        '''
25
        # DPID-compatible hashable identifier: opaque 64-bit unsigned int
26
        self.dpid = dpid
27
28
    def __str__(self):
29
        '''String conversion.
30

31
        @return str dpid as string
32
        '''
33
        return str(self.dpid)
34
35
    def name_str(self):
36
        '''Name conversion.
37 8b5062a3 Brandon Heller

38 433a7cc8 Brandon Heller
        @return name name as string
39
        '''
40
        return str(self.dpid)
41
42
    def ip_str(self):
43
        '''Name conversion.
44

45
        @return ip ip as string
46
        '''
47
        hi = (self.dpid & 0xff0000) >> 16
48
        mid = (self.dpid & 0xff00) >> 8
49
        lo = self.dpid & 0xff
50
        return "10.%i.%i.%i" % (hi, mid, lo)
51 8b5062a3 Brandon Heller
52 433a7cc8 Brandon Heller
53
class Node(object):
54
    '''Node-specific vertex metadata for a Topo object.'''
55
56
    def __init__(self, connected = False, admin_on = True,
57
                 power_on = True, fault = False, is_switch = True):
58 8b5062a3 Brandon Heller
        '''Init.
59

60 433a7cc8 Brandon Heller
        @param connected actively connected to controller
61
        @param admin_on administratively on or off
62
        @param power_on powered on or off
63
        @param fault fault seen on node
64
        @param is_switch switch or host
65
        '''
66
        self.connected = connected
67
        self.admin_on = admin_on
68
        self.power_on = power_on
69
        self.fault = fault
70
        self.is_switch = is_switch
71 8b5062a3 Brandon Heller
72
73 433a7cc8 Brandon Heller
class Edge(object):
74
    '''Edge-specific metadata for a StructuredTopo graph.'''
75
76
    def __init__(self, admin_on = True, power_on = True, fault = False):
77
        '''Init.
78

79
        @param admin_on administratively on or off; defaults to True
80
        @param power_on powered on or off; defaults to True
81
        @param fault fault seen on edge; defaults to False
82
        '''
83
        self.admin_on = admin_on
84
        self.power_on = power_on
85
        self.fault = fault
86
87
88
class Topo(object):
89
    '''Data center network representation for structured multi-trees.'''
90 723d068c Brandon Heller
91 433a7cc8 Brandon Heller
    def __init__(self):
92
        '''Create Topo object.
93

94
        '''
95
        self.g = Graph()
96
        self.node_info = {} # dpids hash to Node objects
97
        self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
98
        self.ports = {} # ports[src][dst] is port on src that connects to dst
99
        self.id_gen = NodeID # class used to generate dpid
100
101
    def _add_node(self, dpid, node):
102
        '''Add Node to graph.
103

104
        @param dpid dpid
105
        @param node Node object
106
        '''
107
        self.g.add_node(dpid)
108
        self.node_info[dpid] = node
109
110
    def _add_edge(self, src, dst, edge):
111
        '''Add edge (Node, Node) to graph.
112

113
        @param src src dpid
114
        @param dst dst dpid
115
        @param edge Edge object
116
        '''
117
        src, dst = tuple(sorted([src, dst]))
118
        self.g.add_edge(src, dst)
119
        self.edge_info[(src, dst)] = edge
120
        self._add_port(src, dst)
121 8b5062a3 Brandon Heller
122 433a7cc8 Brandon Heller
    def _add_port(self, src, dst):
123
        '''Generate port mapping for new edge.
124

125
        @param src source switch DPID
126
        @param dst destination switch DPID
127
        '''
128
        if src not in self.ports:
129
            self.ports[src] = {}
130
        if dst not in self.ports[src]:
131
            self.ports[src][dst] = len(self.ports[src]) # num outlinks
132
133
        if dst not in self.ports:
134
            self.ports[dst] = {}
135
        if src not in self.ports[dst]:
136
            self.ports[dst][src] = len(self.ports[dst]) # num outlinks
137
138
    def node_enabled(self, dpid):
139
        '''Is node connected, admin on, powered on, and fault-free?
140

141
        @param dpid dpid
142

143
        @return bool node is enabled
144
        '''
145
        ni = self.node_info[dpid]
146
        return ni.connected and ni.admin_on and ni.power_on and not ni.fault
147
148
    def nodes_enabled(self, dpids, enabled = True):
149
        '''Return subset of enabled nodes
150

151
        @param dpids list of dpids
152
        @param enabled only return enabled nodes?
153

154
        @return dpids filtered list of dpids
155
        '''
156
        if enabled:
157
            return [n for n in dpids if self.node_enabled(n)]
158
        else:
159
            return dpids
160
161
    def nodes(self, enabled = True):
162
        '''Return graph nodes.
163

164
        @param enabled only return enabled nodes?
165

166
        @return dpids list of dpids
167
        '''
168
        return self.nodes_enabled(self.g.nodes(), enabled)
169
170
    def nodes_str(self, dpids):
171
        '''Return string of custom-encoded nodes.
172

173
        @param dpids list of dpids
174

175
        @return str string
176
        '''
177
        return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
178
179
    def switches(self, enabled = True):
180
        '''Return switches.
181

182
        @param enabled only return enabled nodes?
183

184
        @return dpids list of dpids
185
        '''
186 723d068c Brandon Heller
187 433a7cc8 Brandon Heller
        def is_switch(n):
188
            '''Returns true if node is a switch.'''
189
            return self.node_info[n].is_switch
190
191
        nodes = [n for n in self.g.nodes() if is_switch(n)]
192
        return self.nodes_enabled(nodes, enabled)
193
194
    def hosts(self, enabled = True):
195
        '''Return hosts.
196

197
        @param enabled only return enabled nodes?
198

199
        @return dpids list of dpids
200
        '''
201 723d068c Brandon Heller
202 433a7cc8 Brandon Heller
        def is_host(n):
203
            '''Returns true if node is a host.'''
204
            return not self.node_info[n].is_switch
205
206
        nodes = [n for n in self.g.nodes() if is_host(n)]
207
        return self.nodes_enabled(nodes, enabled)
208
209
    def edge_enabled(self, edge):
210
        '''Is edge admin on, powered on, and fault-free?
211

212
        @param edge (src, dst) dpid tuple
213

214
        @return bool edge is enabled
215
        '''
216
        src, dst = edge
217
        src, dst = tuple(sorted([src, dst]))
218
        ei = self.edge_info[tuple(sorted([src, dst]))]
219
        return ei.admin_on and ei.power_on and not ei.fault
220
221
    def edges_enabled(self, edges, enabled = True):
222
        '''Return subset of enabled edges
223

224
        @param edges list of edges
225
        @param enabled only return enabled edges?
226

227
        @return edges filtered list of edges
228
        '''
229
        if enabled:
230
            return [e for e in edges if self.edge_enabled(e)]
231
        else:
232
            return edges
233
234
    def edges(self, enabled = True):
235
        '''Return edges.
236

237
        @param enabled only return enabled edges?
238

239
        @return edges list of dpid pairs
240
        '''
241
        return self.edges_enabled(self.g.edges(), enabled)
242
243
    def edges_str(self, dpid_pairs):
244
        '''Return string of custom-encoded node pairs.
245

246
        @param dpid_pairs list of dpid pairs (src, dst)
247

248
        @return str string
249
        '''
250
        edges = []
251
        for pair in dpid_pairs:
252
            src, dst = pair
253
            src = str(self.id_gen(dpid = src))
254
            dst = str(self.id_gen(dpid = dst))
255
            edges.append((src, dst))
256
        return edges
257
258
    def port(self, src, dst):
259
        '''Get port number.
260 8b5062a3 Brandon Heller

261
        @param src source switch DPID
262
        @param dst destination switch DPID
263
        @return tuple (src_port, dst_port):
264
            src_port: port on source switch leading to the destination switch
265
            dst_port: port on destination switch leading to the source switch
266
        '''
267 433a7cc8 Brandon Heller
        if src in self.ports and dst in self.ports[src]:
268
            assert dst in self.ports and src in self.ports[dst]
269
            return (self.ports[src][dst], self.ports[dst][src])
270 8b5062a3 Brandon Heller
271 433a7cc8 Brandon Heller
    def enable_edges(self):
272
        '''Enable all edges in the network graph.
273 8b5062a3 Brandon Heller

274 433a7cc8 Brandon Heller
        Set admin on, power on, and fault off.
275
        '''
276
        for e in self.g.edges():
277
            src, dst = e
278
            ei = self.edge_info[tuple(sorted([src, dst]))]
279
            ei.admin_on = True
280
            ei.power_on = True
281
            ei.fault = False
282 8b5062a3 Brandon Heller
283 433a7cc8 Brandon Heller
    def enable_nodes(self):
284
        '''Enable all nodes in the network graph.
285 8b5062a3 Brandon Heller

286 433a7cc8 Brandon Heller
        Set connected on, admin on, power on, and fault off.
287
        '''
288
        for node in self.g.nodes():
289
            ni = self.node_info[node]
290
            ni.connected = True
291
            ni.admin_on = True
292
            ni.power_on = True
293
            ni.fault = False
294
295
    def enable_all(self):
296
        '''Enable all nodes and edges in the network graph.'''
297
        self.enable_nodes()
298
        self.enable_edges()
299 8b5062a3 Brandon Heller
300
    def name(self, dpid):
301
        '''Get string name of node ID.
302

303
        @param dpid DPID of host or switch
304
        @return name_str string name with no dashes
305
        '''
306
        return self.id_gen(dpid = dpid).name_str()
307
308
    def ip(self, dpid):
309
        '''Get IP dotted-decimal string of node ID.
310

311
        @param dpid DPID of host or switch
312
        @return ip_str
313
        '''
314 433a7cc8 Brandon Heller
        return self.id_gen(dpid = dpid).ip_str()
315
316
317
class SingleSwitchTopo(Topo):
318
    '''Single switch connected to k hosts.'''
319
320
    def __init__(self, k = 2, enable_all = True):
321
        '''Init.
322

323
        @param k number of hosts
324
        @param enable_all enables all nodes and switches?
325
        '''
326
        super(SingleSwitchTopo, self).__init__()
327
328
        self.k = k
329
330 54037995 Brandon Heller
        self._add_node(1, Node())
331
        hosts = range(2, k + 2)
332 433a7cc8 Brandon Heller
        for h in hosts:
333
            self._add_node(h, Node(is_switch = False))
334 54037995 Brandon Heller
            self._add_edge(h, 1, Edge())
335 433a7cc8 Brandon Heller
336
        if enable_all:
337
            self.enable_all()
338
339
340 6d2cd77b Brandon Heller
class SingleSwitchReversedTopo(SingleSwitchTopo):
341
    '''Single switch connected to k hosts, with reversed ports.
342

343
    The lowest-numbered host is connected to the highest-numbered port.
344

345
    Useful to verify that Mininet properly handles custom port numberings.
346
    '''
347
348
    def port(self, src, dst):
349
        '''Get port number.
350

351
        @param src source switch DPID
352
        @param dst destination switch DPID
353
        @return tuple (src_port, dst_port):
354
            src_port: port on source switch leading to the destination switch
355
            dst_port: port on destination switch leading to the source switch
356
        '''
357
        if src == 1:
358
            if dst in range(2, self.k + 2):
359
                dst_index = dst - 2
360
                highest = self.k - 1
361
                return (highest - dst_index, 0)
362
            else:
363
                raise Exception('unexpected dst: %i' % dst)
364
        elif src in range(2, self.k + 2):
365
            if dst == 1:
366
                raise Exception('unexpected dst: %i' % dst)
367
            else:
368
                src_index = src - 2
369
                highest = self.k - 1
370
                return (0, highest - src_index)
371
372
373 433a7cc8 Brandon Heller
class LinearTopo(Topo):
374
    '''Linear topology of k switches, with one host per switch.'''
375
376
    def __init__(self, k = 2, enable_all = True):
377
        '''Init.
378

379
        @param k number of switches (and hosts too)
380
        @param enable_all enables all nodes and switches?
381
        '''
382
        super(LinearTopo, self).__init__()
383
384
        self.k = k
385
386 54037995 Brandon Heller
        switches = range(1, k + 1)
387 433a7cc8 Brandon Heller
        for s in switches:
388
            h = s + k
389
            self._add_node(s, Node())
390
            self._add_node(h, Node(is_switch = False))
391
            self._add_edge(s, h, Edge())
392
        for s in switches:
393 54037995 Brandon Heller
            if s != k:
394 433a7cc8 Brandon Heller
                self._add_edge(s, s + 1, Edge())
395
396
        if enable_all:
397 723d068c Brandon Heller
            self.enable_all()