Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 723d068c

History | View | Annotate | Download (11 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):
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

    
122
    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

    
187
        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

    
202
        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

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

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

274
        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

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

286
        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

    
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
        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
        self._add_node(1, Node())
331
        hosts = range(2, k + 2)
332
        for h in hosts:
333
            self._add_node(h, Node(is_switch = False))
334
            self._add_edge(h, 1, Edge())
335

    
336
        if enable_all:
337
            self.enable_all()
338

    
339

    
340
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
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
        switches = range(1, k + 1)
387
        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
            if s != k:
394
                self._add_edge(s, s + 1, Edge())
395

    
396
        if enable_all:
397
            self.enable_all()