Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 80be5642

History | View | Annotate | Download (11.1 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 80be5642 Bob Lantz
    def add_node(self, dpid, node):
102 433a7cc8 Brandon Heller
        '''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 80be5642 Bob Lantz
    def add_edge(self, src, dst, edge = None):
111 433a7cc8 Brandon Heller
        '''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 befa1310 Brandon Heller
        if not edge:
120
            edge = Edge()
121 433a7cc8 Brandon Heller
        self.edge_info[(src, dst)] = edge
122 80be5642 Bob Lantz
        self.add_port(src, dst)
123 8b5062a3 Brandon Heller
124 80be5642 Bob Lantz
    def add_port(self, src, dst):
125 433a7cc8 Brandon Heller
        '''Generate port mapping for new edge.
126

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

143
        @param dpid dpid
144

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

153
        @param dpids list of dpids
154
        @param enabled only return enabled nodes?
155

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

166
        @param enabled only return enabled nodes?
167

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

175
        @param dpids list of dpids
176

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

184
        @param enabled only return enabled nodes?
185

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

199
        @param enabled only return enabled nodes?
200

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

214
        @param edge (src, dst) dpid tuple
215

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

226
        @param edges list of edges
227
        @param enabled only return enabled edges?
228

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

239
        @param enabled only return enabled edges?
240

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

248
        @param dpid_pairs list of dpid pairs (src, dst)
249

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

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

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

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

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

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

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

345
    The lowest-numbered host is connected to the highest-numbered port.
346

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

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

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