forked from mininet/mininet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtopo.py
More file actions
405 lines (308 loc) · 11.4 KB
/
topo.py
File metadata and controls
405 lines (308 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#!/usr/bin/env python
'''@package topo
Network topology creation.
@author Brandon Heller ([email protected])
This package includes code to represent network topologies.
A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
'''
# BL: we may have to fix compatibility here.
# networkx is also a fairly heavyweight dependency
# from networkx.classes.graph import Graph
from networkx import Graph
from mininet.node import SWITCH_PORT_BASE
class NodeID(object):
'''Topo node identifier.'''
def __init__(self, dpid = None):
'''Init.
@param dpid dpid
'''
# DPID-compatible hashable identifier: opaque 64-bit unsigned int
self.dpid = dpid
def __str__(self):
'''String conversion.
@return str dpid as string
'''
return str(self.dpid)
def name_str(self):
'''Name conversion.
@return name name as string
'''
return str(self.dpid)
def ip_str(self):
'''Name conversion.
@return ip ip as string
'''
hi = (self.dpid & 0xff0000) >> 16
mid = (self.dpid & 0xff00) >> 8
lo = self.dpid & 0xff
return "10.%i.%i.%i" % (hi, mid, lo)
class Node(object):
'''Node-specific vertex metadata for a Topo object.'''
def __init__(self, connected = False, admin_on = True,
power_on = True, fault = False, is_switch = True):
'''Init.
@param connected actively connected to controller
@param admin_on administratively on or off
@param power_on powered on or off
@param fault fault seen on node
@param is_switch switch or host
'''
self.connected = connected
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
self.is_switch = is_switch
class Edge(object):
'''Edge-specific metadata for a StructuredTopo graph.'''
def __init__(self, admin_on = True, power_on = True, fault = False):
'''Init.
@param admin_on administratively on or off; defaults to True
@param power_on powered on or off; defaults to True
@param fault fault seen on edge; defaults to False
'''
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
class Topo(object):
'''Data center network representation for structured multi-trees.'''
def __init__(self):
'''Create Topo object.
'''
self.g = Graph()
self.node_info = {} # dpids hash to Node objects
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
self.ports = {} # ports[src][dst] is port on src that connects to dst
self.id_gen = NodeID # class used to generate dpid
def add_node(self, dpid, node):
'''Add Node to graph.
@param dpid dpid
@param node Node object
'''
self.g.add_node(dpid)
self.node_info[dpid] = node
def add_edge(self, src, dst, edge = None):
'''Add edge (Node, Node) to graph.
@param src src dpid
@param dst dst dpid
@param edge Edge object
'''
src, dst = tuple(sorted([src, dst]))
self.g.add_edge(src, dst)
if not edge:
edge = Edge()
self.edge_info[(src, dst)] = edge
self.add_port(src, dst)
def add_port(self, src, dst):
'''Generate port mapping for new edge.
@param src source switch DPID
@param dst destination switch DPID
'''
src_base = SWITCH_PORT_BASE if self.is_switch(src) else 0
dst_base = SWITCH_PORT_BASE if self.is_switch(dst) else 0
if src not in self.ports:
self.ports[src] = {}
if dst not in self.ports[src]:
# num outlinks
self.ports[src][dst] = len(self.ports[src]) + src_base
if dst not in self.ports:
self.ports[dst] = {}
if src not in self.ports[dst]:
# num outlinks
self.ports[dst][src] = len(self.ports[dst]) + dst_base
def node_enabled(self, dpid):
'''Is node connected, admin on, powered on, and fault-free?
@param dpid dpid
@return bool node is enabled
'''
ni = self.node_info[dpid]
return ni.connected and ni.admin_on and ni.power_on and not ni.fault
def nodes_enabled(self, dpids, enabled = True):
'''Return subset of enabled nodes
@param dpids list of dpids
@param enabled only return enabled nodes?
@return dpids filtered list of dpids
'''
if enabled:
return [n for n in dpids if self.node_enabled(n)]
else:
return dpids
def nodes(self, enabled = True):
'''Return graph nodes.
@param enabled only return enabled nodes?
@return dpids list of dpids
'''
return self.nodes_enabled(self.g.nodes(), enabled)
def nodes_str(self, dpids):
'''Return string of custom-encoded nodes.
@param dpids list of dpids
@return str string
'''
return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
def is_switch(self, n):
'''Returns true if node is a switch.'''
return self.node_info[n].is_switch
def switches(self, enabled = True):
'''Return switches.
@param enabled only return enabled nodes?
@return dpids list of dpids
'''
nodes = [n for n in self.g.nodes() if self.is_switch(n)]
return self.nodes_enabled(nodes, enabled)
def hosts(self, enabled = True):
'''Return hosts.
@param enabled only return enabled nodes?
@return dpids list of dpids
'''
def is_host(n):
'''Returns true if node is a host.'''
return not self.node_info[n].is_switch
nodes = [n for n in self.g.nodes() if is_host(n)]
return self.nodes_enabled(nodes, enabled)
def edge_enabled(self, edge):
'''Is edge admin on, powered on, and fault-free?
@param edge (src, dst) dpid tuple
@return bool edge is enabled
'''
src, dst = edge
src, dst = tuple(sorted([src, dst]))
ei = self.edge_info[tuple(sorted([src, dst]))]
return ei.admin_on and ei.power_on and not ei.fault
def edges_enabled(self, edges, enabled = True):
'''Return subset of enabled edges
@param edges list of edges
@param enabled only return enabled edges?
@return edges filtered list of edges
'''
if enabled:
return [e for e in edges if self.edge_enabled(e)]
else:
return edges
def edges(self, enabled = True):
'''Return edges.
@param enabled only return enabled edges?
@return edges list of dpid pairs
'''
return self.edges_enabled(self.g.edges(), enabled)
def edges_str(self, dpid_pairs):
'''Return string of custom-encoded node pairs.
@param dpid_pairs list of dpid pairs (src, dst)
@return str string
'''
edges = []
for pair in dpid_pairs:
src, dst = pair
src = str(self.id_gen(dpid = src))
dst = str(self.id_gen(dpid = dst))
edges.append((src, dst))
return edges
def port(self, src, dst):
'''Get port number.
@param src source switch DPID
@param dst destination switch DPID
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src in self.ports and dst in self.ports[src]:
assert dst in self.ports and src in self.ports[dst]
return (self.ports[src][dst], self.ports[dst][src])
def enable_edges(self):
'''Enable all edges in the network graph.
Set admin on, power on, and fault off.
'''
for e in self.g.edges():
src, dst = e
ei = self.edge_info[tuple(sorted([src, dst]))]
ei.admin_on = True
ei.power_on = True
ei.fault = False
def enable_nodes(self):
'''Enable all nodes in the network graph.
Set connected on, admin on, power on, and fault off.
'''
for node in self.g.nodes():
ni = self.node_info[node]
ni.connected = True
ni.admin_on = True
ni.power_on = True
ni.fault = False
def enable_all(self):
'''Enable all nodes and edges in the network graph.'''
self.enable_nodes()
self.enable_edges()
def name(self, dpid):
'''Get string name of node ID.
@param dpid DPID of host or switch
@return name_str string name with no dashes
'''
return self.id_gen(dpid = dpid).name_str()
def ip(self, dpid):
'''Get IP dotted-decimal string of node ID.
@param dpid DPID of host or switch
@return ip_str
'''
return self.id_gen(dpid = dpid).ip_str()
class SingleSwitchTopo(Topo):
'''Single switch connected to k hosts.'''
def __init__(self, k = 2, enable_all = True):
'''Init.
@param k number of hosts
@param enable_all enables all nodes and switches?
'''
super(SingleSwitchTopo, self).__init__()
self.k = k
self.add_node(1, Node())
hosts = range(2, k + 2)
for h in hosts:
self.add_node(h, Node(is_switch = False))
self.add_edge(h, 1, Edge())
if enable_all:
self.enable_all()
class SingleSwitchReversedTopo(SingleSwitchTopo):
'''Single switch connected to k hosts, with reversed ports.
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port numberings.
'''
def port(self, src, dst):
'''Get port number.
@param src source switch DPID
@param dst destination switch DPID
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src == 1:
if dst in range(2, self.k + 2):
dst_index = dst - 2
highest = self.k - 1
return (highest - dst_index, 0)
else:
raise Exception('unexpected dst: %i' % dst)
elif src in range(2, self.k + 2):
if dst == 1:
raise Exception('unexpected dst: %i' % dst)
else:
src_index = src - 2
highest = self.k - 1
return (0, highest - src_index)
class LinearTopo(Topo):
'''Linear topology of k switches, with one host per switch.'''
def __init__(self, k = 2, enable_all = True):
'''Init.
@param k number of switches (and hosts too)
@param enable_all enables all nodes and switches?
'''
super(LinearTopo, self).__init__()
self.k = k
switches = range(1, k + 1)
for s in switches:
h = s + k
self.add_node(s, Node())
self.add_node(h, Node(is_switch = False))
self.add_edge(s, h, Edge())
for s in switches:
if s != k:
self.add_edge(s, s + 1, Edge())
if enable_all:
self.enable_all()