Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 80be5642

History | View | Annotate | Download (11.1 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 networkx.classes.graph import Graph
15

    
16

    
17
class NodeID(object):
18
    '''Topo node identifier.'''
19

    
20
    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

38
        @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

    
52

    
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
        '''Init.
59

60
        @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

    
72

    
73
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

    
91
    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 = None):
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
        if not edge:
120
            edge = Edge()
121
        self.edge_info[(src, dst)] = edge
122
        self.add_port(src, dst)
123

    
124
    def add_port(self, src, dst):
125
        '''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

    
189
        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

    
204
        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

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
        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

    
273
    def enable_edges(self):
274
        '''Enable all edges in the network graph.
275

276
        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

    
285
    def enable_nodes(self):
286
        '''Enable all nodes in the network graph.
287

288
        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

    
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
        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
        self.add_node(1, Node())
333
        hosts = range(2, k + 2)
334
        for h in hosts:
335
            self.add_node(h, Node(is_switch = False))
336
            self.add_edge(h, 1, Edge())
337

    
338
        if enable_all:
339
            self.enable_all()
340

    
341

    
342
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
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
        switches = range(1, k + 1)
389
        for s in switches:
390
            h = s + k
391
            self.add_node(s, Node())
392
            self.add_node(h, Node(is_switch = False))
393
            self.add_edge(s, h, Edge())
394
        for s in switches:
395
            if s != k:
396
                self.add_edge(s, s + 1, Edge())
397

    
398
        if enable_all:
399
            self.enable_all()