Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 8a130dea

History | View | Annotate | Download (11.4 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
# BL: we may have to fix compatibility here.
15
# networkx is also a fairly heavyweight dependency
16
# from networkx.classes.graph import Graph
17

    
18
from networkx import Graph
19
from mininet.node import SWITCH_PORT_BASE
20

    
21
class NodeID(object):
22
    '''Topo node identifier.'''
23

    
24
    def __init__(self, dpid = None):
25
        '''Init.
26

27
        @param dpid dpid
28
        '''
29
        # DPID-compatible hashable identifier: opaque 64-bit unsigned int
30
        self.dpid = dpid
31

    
32
    def __str__(self):
33
        '''String conversion.
34

35
        @return str dpid as string
36
        '''
37
        return str(self.dpid)
38

    
39
    def name_str(self):
40
        '''Name conversion.
41

42
        @return name name as string
43
        '''
44
        return str(self.dpid)
45

    
46
    def ip_str(self):
47
        '''Name conversion.
48

49
        @return ip ip as string
50
        '''
51
        hi = (self.dpid & 0xff0000) >> 16
52
        mid = (self.dpid & 0xff00) >> 8
53
        lo = self.dpid & 0xff
54
        return "10.%i.%i.%i" % (hi, mid, lo)
55

    
56

    
57
class Node(object):
58
    '''Node-specific vertex metadata for a Topo object.'''
59

    
60
    def __init__(self, connected = False, admin_on = True,
61
                 power_on = True, fault = False, is_switch = True):
62
        '''Init.
63

64
        @param connected actively connected to controller
65
        @param admin_on administratively on or off
66
        @param power_on powered on or off
67
        @param fault fault seen on node
68
        @param is_switch switch or host
69
        '''
70
        self.connected = connected
71
        self.admin_on = admin_on
72
        self.power_on = power_on
73
        self.fault = fault
74
        self.is_switch = is_switch
75

    
76

    
77
class Edge(object):
78
    '''Edge-specific metadata for a StructuredTopo graph.'''
79

    
80
    def __init__(self, admin_on = True, power_on = True, fault = False):
81
        '''Init.
82

83
        @param admin_on administratively on or off; defaults to True
84
        @param power_on powered on or off; defaults to True
85
        @param fault fault seen on edge; defaults to False
86
        '''
87
        self.admin_on = admin_on
88
        self.power_on = power_on
89
        self.fault = fault
90

    
91

    
92
class Topo(object):
93
    '''Data center network representation for structured multi-trees.'''
94

    
95
    def __init__(self):
96
        '''Create Topo object.
97

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

    
105
    def add_node(self, dpid, node):
106
        '''Add Node to graph.
107

108
        @param dpid dpid
109
        @param node Node object
110
        '''
111
        self.g.add_node(dpid)
112
        self.node_info[dpid] = node
113

    
114
    def add_edge(self, src, dst, edge = None):
115
        '''Add edge (Node, Node) to graph.
116

117
        @param src src dpid
118
        @param dst dst dpid
119
        @param edge Edge object
120
        '''
121
        src, dst = tuple(sorted([src, dst]))
122
        self.g.add_edge(src, dst)
123
        if not edge:
124
            edge = Edge()
125
        self.edge_info[(src, dst)] = edge
126
        self.add_port(src, dst)
127

    
128
    def add_port(self, src, dst):
129
        '''Generate port mapping for new edge.
130

131
        @param src source switch DPID
132
        @param dst destination switch DPID
133
        '''
134
        src_base = SWITCH_PORT_BASE if self.is_switch(src) else 0
135
        dst_base = SWITCH_PORT_BASE if self.is_switch(dst) else 0
136
        if src not in self.ports:
137
            self.ports[src] = {}
138
        if dst not in self.ports[src]:
139
            # num outlinks
140
            self.ports[src][dst] = len(self.ports[src]) + src_base
141
        if dst not in self.ports:
142
            self.ports[dst] = {}
143
        if src not in self.ports[dst]:
144
            # num outlinks
145
            self.ports[dst][src] = len(self.ports[dst]) + dst_base
146

    
147
    def node_enabled(self, dpid):
148
        '''Is node connected, admin on, powered on, and fault-free?
149

150
        @param dpid dpid
151

152
        @return bool node is enabled
153
        '''
154
        ni = self.node_info[dpid]
155
        return ni.connected and ni.admin_on and ni.power_on and not ni.fault
156

    
157
    def nodes_enabled(self, dpids, enabled = True):
158
        '''Return subset of enabled nodes
159

160
        @param dpids list of dpids
161
        @param enabled only return enabled nodes?
162

163
        @return dpids filtered list of dpids
164
        '''
165
        if enabled:
166
            return [n for n in dpids if self.node_enabled(n)]
167
        else:
168
            return dpids
169

    
170
    def nodes(self, enabled = True):
171
        '''Return graph nodes.
172

173
        @param enabled only return enabled nodes?
174

175
        @return dpids list of dpids
176
        '''
177
        return self.nodes_enabled(self.g.nodes(), enabled)
178

    
179
    def nodes_str(self, dpids):
180
        '''Return string of custom-encoded nodes.
181

182
        @param dpids list of dpids
183

184
        @return str string
185
        '''
186
        return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
187

    
188
    def is_switch(self, n):
189
        '''Returns true if node is a switch.'''
190
        return self.node_info[n].is_switch
191

    
192
    def switches(self, enabled = True):
193
        '''Return switches.
194

195
        @param enabled only return enabled nodes?
196

197
        @return dpids list of dpids
198
        '''
199
        nodes = [n for n in self.g.nodes() if self.is_switch(n)]
200
        return self.nodes_enabled(nodes, enabled)
201

    
202
    def hosts(self, enabled = True):
203
        '''Return hosts.
204

205
        @param enabled only return enabled nodes?
206

207
        @return dpids list of dpids
208
        '''
209

    
210
        def is_host(n):
211
            '''Returns true if node is a host.'''
212
            return not self.node_info[n].is_switch
213

    
214
        nodes = [n for n in self.g.nodes() if is_host(n)]
215
        return self.nodes_enabled(nodes, enabled)
216

    
217
    def edge_enabled(self, edge):
218
        '''Is edge admin on, powered on, and fault-free?
219

220
        @param edge (src, dst) dpid tuple
221

222
        @return bool edge is enabled
223
        '''
224
        src, dst = edge
225
        src, dst = tuple(sorted([src, dst]))
226
        ei = self.edge_info[tuple(sorted([src, dst]))]
227
        return ei.admin_on and ei.power_on and not ei.fault
228

    
229
    def edges_enabled(self, edges, enabled = True):
230
        '''Return subset of enabled edges
231

232
        @param edges list of edges
233
        @param enabled only return enabled edges?
234

235
        @return edges filtered list of edges
236
        '''
237
        if enabled:
238
            return [e for e in edges if self.edge_enabled(e)]
239
        else:
240
            return edges
241

    
242
    def edges(self, enabled = True):
243
        '''Return edges.
244

245
        @param enabled only return enabled edges?
246

247
        @return edges list of dpid pairs
248
        '''
249
        return self.edges_enabled(self.g.edges(), enabled)
250

    
251
    def edges_str(self, dpid_pairs):
252
        '''Return string of custom-encoded node pairs.
253

254
        @param dpid_pairs list of dpid pairs (src, dst)
255

256
        @return str string
257
        '''
258
        edges = []
259
        for pair in dpid_pairs:
260
            src, dst = pair
261
            src = str(self.id_gen(dpid = src))
262
            dst = str(self.id_gen(dpid = dst))
263
            edges.append((src, dst))
264
        return edges
265

    
266
    def port(self, src, dst):
267
        '''Get port number.
268

269
        @param src source switch DPID
270
        @param dst destination switch DPID
271
        @return tuple (src_port, dst_port):
272
            src_port: port on source switch leading to the destination switch
273
            dst_port: port on destination switch leading to the source switch
274
        '''
275
        if src in self.ports and dst in self.ports[src]:
276
            assert dst in self.ports and src in self.ports[dst]
277
            return (self.ports[src][dst], self.ports[dst][src])
278

    
279
    def enable_edges(self):
280
        '''Enable all edges in the network graph.
281

282
        Set admin on, power on, and fault off.
283
        '''
284
        for e in self.g.edges():
285
            src, dst = e
286
            ei = self.edge_info[tuple(sorted([src, dst]))]
287
            ei.admin_on = True
288
            ei.power_on = True
289
            ei.fault = False
290

    
291
    def enable_nodes(self):
292
        '''Enable all nodes in the network graph.
293

294
        Set connected on, admin on, power on, and fault off.
295
        '''
296
        for node in self.g.nodes():
297
            ni = self.node_info[node]
298
            ni.connected = True
299
            ni.admin_on = True
300
            ni.power_on = True
301
            ni.fault = False
302

    
303
    def enable_all(self):
304
        '''Enable all nodes and edges in the network graph.'''
305
        self.enable_nodes()
306
        self.enable_edges()
307

    
308
    def name(self, dpid):
309
        '''Get string name of node ID.
310

311
        @param dpid DPID of host or switch
312
        @return name_str string name with no dashes
313
        '''
314
        return self.id_gen(dpid = dpid).name_str()
315

    
316
    def ip(self, dpid):
317
        '''Get IP dotted-decimal string of node ID.
318

319
        @param dpid DPID of host or switch
320
        @return ip_str
321
        '''
322
        return self.id_gen(dpid = dpid).ip_str()
323

    
324

    
325
class SingleSwitchTopo(Topo):
326
    '''Single switch connected to k hosts.'''
327

    
328
    def __init__(self, k = 2, enable_all = True):
329
        '''Init.
330

331
        @param k number of hosts
332
        @param enable_all enables all nodes and switches?
333
        '''
334
        super(SingleSwitchTopo, self).__init__()
335

    
336
        self.k = k
337

    
338
        self.add_node(1, Node())
339
        hosts = range(2, k + 2)
340
        for h in hosts:
341
            self.add_node(h, Node(is_switch = False))
342
            self.add_edge(h, 1, Edge())
343

    
344
        if enable_all:
345
            self.enable_all()
346

    
347

    
348
class SingleSwitchReversedTopo(SingleSwitchTopo):
349
    '''Single switch connected to k hosts, with reversed ports.
350

351
    The lowest-numbered host is connected to the highest-numbered port.
352

353
    Useful to verify that Mininet properly handles custom port numberings.
354
    '''
355

    
356
    def port(self, src, dst):
357
        '''Get port number.
358

359
        @param src source switch DPID
360
        @param dst destination switch DPID
361
        @return tuple (src_port, dst_port):
362
            src_port: port on source switch leading to the destination switch
363
            dst_port: port on destination switch leading to the source switch
364
        '''
365
        if src == 1:
366
            if dst in range(2, self.k + 2):
367
                dst_index = dst - 2
368
                highest = self.k - 1
369
                return (highest - dst_index, 0)
370
            else:
371
                raise Exception('unexpected dst: %i' % dst)
372
        elif src in range(2, self.k + 2):
373
            if dst == 1:
374
                raise Exception('unexpected dst: %i' % dst)
375
            else:
376
                src_index = src - 2
377
                highest = self.k - 1
378
                return (0, highest - src_index)
379

    
380

    
381
class LinearTopo(Topo):
382
    '''Linear topology of k switches, with one host per switch.'''
383

    
384
    def __init__(self, k = 2, enable_all = True):
385
        '''Init.
386

387
        @param k number of switches (and hosts too)
388
        @param enable_all enables all nodes and switches?
389
        '''
390
        super(LinearTopo, self).__init__()
391

    
392
        self.k = k
393

    
394
        switches = range(1, k + 1)
395
        for s in switches:
396
            h = s + k
397
            self.add_node(s, Node())
398
            self.add_node(h, Node(is_switch = False))
399
            self.add_edge(s, h, Edge())
400
        for s in switches:
401
            if s != k:
402
                self.add_edge(s, s + 1, Edge())
403

    
404
        if enable_all:
405
            self.enable_all()