Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 8a130dea

History | View | Annotate | Download (11.4 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 24baea73 Bob Lantz
# 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 dd159b4a Bob Lantz
from mininet.node import SWITCH_PORT_BASE
20 8b5062a3 Brandon Heller
21 433a7cc8 Brandon Heller
class NodeID(object):
22
    '''Topo node identifier.'''
23 8b5062a3 Brandon Heller
24 433a7cc8 Brandon Heller
    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 8b5062a3 Brandon Heller

42 433a7cc8 Brandon Heller
        @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 8b5062a3 Brandon Heller
56 433a7cc8 Brandon Heller
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 8b5062a3 Brandon Heller
        '''Init.
63

64 433a7cc8 Brandon Heller
        @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 8b5062a3 Brandon Heller
76
77 433a7cc8 Brandon Heller
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 723d068c Brandon Heller
95 433a7cc8 Brandon Heller
    def __init__(self):
96
        '''Create Topo object.
97

98
        '''
99
        self.g = Graph()
100 0774c8bb Bob Lantz
        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 433a7cc8 Brandon Heller
105 80be5642 Bob Lantz
    def add_node(self, dpid, node):
106 433a7cc8 Brandon Heller
        '''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 80be5642 Bob Lantz
    def add_edge(self, src, dst, edge = None):
115 433a7cc8 Brandon Heller
        '''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 befa1310 Brandon Heller
        if not edge:
124
            edge = Edge()
125 433a7cc8 Brandon Heller
        self.edge_info[(src, dst)] = edge
126 80be5642 Bob Lantz
        self.add_port(src, dst)
127 8b5062a3 Brandon Heller
128 80be5642 Bob Lantz
    def add_port(self, src, dst):
129 433a7cc8 Brandon Heller
        '''Generate port mapping for new edge.
130

131
        @param src source switch DPID
132
        @param dst destination switch DPID
133
        '''
134 dd159b4a Bob Lantz
        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 433a7cc8 Brandon Heller
        if src not in self.ports:
137
            self.ports[src] = {}
138
        if dst not in self.ports[src]:
139 68c89df8 Bob Lantz
            # num outlinks
140 dd159b4a Bob Lantz
            self.ports[src][dst] = len(self.ports[src]) + src_base
141 433a7cc8 Brandon Heller
        if dst not in self.ports:
142
            self.ports[dst] = {}
143
        if src not in self.ports[dst]:
144 68c89df8 Bob Lantz
            # num outlinks
145 dd159b4a Bob Lantz
            self.ports[dst][src] = len(self.ports[dst]) + dst_base
146 433a7cc8 Brandon Heller
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 dd159b4a Bob Lantz
    def is_switch(self, n):
189
        '''Returns true if node is a switch.'''
190
        return self.node_info[n].is_switch
191
192 433a7cc8 Brandon Heller
    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 dd159b4a Bob Lantz
        nodes = [n for n in self.g.nodes() if self.is_switch(n)]
200 433a7cc8 Brandon Heller
        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 723d068c Brandon Heller
210 433a7cc8 Brandon Heller
        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 8b5062a3 Brandon Heller

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 433a7cc8 Brandon Heller
        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 8b5062a3 Brandon Heller
279 433a7cc8 Brandon Heller
    def enable_edges(self):
280
        '''Enable all edges in the network graph.
281 8b5062a3 Brandon Heller

282 433a7cc8 Brandon Heller
        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 8b5062a3 Brandon Heller
291 433a7cc8 Brandon Heller
    def enable_nodes(self):
292
        '''Enable all nodes in the network graph.
293 8b5062a3 Brandon Heller

294 433a7cc8 Brandon Heller
        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 8b5062a3 Brandon Heller
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 433a7cc8 Brandon Heller
        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 80be5642 Bob Lantz
        self.add_node(1, Node())
339 54037995 Brandon Heller
        hosts = range(2, k + 2)
340 433a7cc8 Brandon Heller
        for h in hosts:
341 80be5642 Bob Lantz
            self.add_node(h, Node(is_switch = False))
342
            self.add_edge(h, 1, Edge())
343 433a7cc8 Brandon Heller
344
        if enable_all:
345
            self.enable_all()
346
347
348 6d2cd77b Brandon Heller
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 433a7cc8 Brandon Heller
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 54037995 Brandon Heller
        switches = range(1, k + 1)
395 433a7cc8 Brandon Heller
        for s in switches:
396
            h = s + k
397 80be5642 Bob Lantz
            self.add_node(s, Node())
398
            self.add_node(h, Node(is_switch = False))
399
            self.add_edge(s, h, Edge())
400 433a7cc8 Brandon Heller
        for s in switches:
401 54037995 Brandon Heller
            if s != k:
402 80be5642 Bob Lantz
                self.add_edge(s, s + 1, Edge())
403 433a7cc8 Brandon Heller
404
        if enable_all:
405 723d068c Brandon Heller
            self.enable_all()