Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 54037995

History | View | Annotate | Download (9.89 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 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
    def __init__(self):
91
        '''Create Topo object.
92

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

    
100
    def _add_node(self, dpid, node):
101
        '''Add Node to graph.
102

103
        @param dpid dpid
104
        @param node Node object
105
        '''
106
        self.g.add_node(dpid)
107
        self.node_info[dpid] = node
108

    
109
    def _add_edge(self, src, dst, edge):
110
        '''Add edge (Node, Node) to graph.
111

112
        @param src src dpid
113
        @param dst dst dpid
114
        @param edge Edge object
115
        '''
116
        src, dst = tuple(sorted([src, dst]))
117
        self.g.add_edge(src, dst)
118
        self.edge_info[(src, dst)] = edge
119
        self._add_port(src, dst)
120

    
121
    def _add_port(self, src, dst):
122
        '''Generate port mapping for new edge.
123

124
        @param src source switch DPID
125
        @param dst destination switch DPID
126
        '''
127
        if src not in self.ports:
128
            self.ports[src] = {}
129
        if dst not in self.ports[src]:
130
            self.ports[src][dst] = len(self.ports[src]) # num outlinks
131

    
132
        if dst not in self.ports:
133
            self.ports[dst] = {}
134
        if src not in self.ports[dst]:
135
            self.ports[dst][src] = len(self.ports[dst]) # num outlinks
136

    
137
    def node_enabled(self, dpid):
138
        '''Is node connected, admin on, powered on, and fault-free?
139

140
        @param dpid dpid
141

142
        @return bool node is enabled
143
        '''
144
        ni = self.node_info[dpid]
145
        return ni.connected and ni.admin_on and ni.power_on and not ni.fault
146

    
147
    def nodes_enabled(self, dpids, enabled = True):
148
        '''Return subset of enabled nodes
149

150
        @param dpids list of dpids
151
        @param enabled only return enabled nodes?
152

153
        @return dpids filtered list of dpids
154
        '''
155
        if enabled:
156
            return [n for n in dpids if self.node_enabled(n)]
157
        else:
158
            return dpids
159

    
160
    def nodes(self, enabled = True):
161
        '''Return graph nodes.
162

163
        @param enabled only return enabled nodes?
164

165
        @return dpids list of dpids
166
        '''
167
        return self.nodes_enabled(self.g.nodes(), enabled)
168

    
169
    def nodes_str(self, dpids):
170
        '''Return string of custom-encoded nodes.
171

172
        @param dpids list of dpids
173

174
        @return str string
175
        '''
176
        return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
177

    
178
    def switches(self, enabled = True):
179
        '''Return switches.
180

181
        @param enabled only return enabled nodes?
182

183
        @return dpids list of dpids
184
        '''
185
        def is_switch(n):
186
            '''Returns true if node is a switch.'''
187
            return self.node_info[n].is_switch
188

    
189
        nodes = [n for n in self.g.nodes() if is_switch(n)]
190
        return self.nodes_enabled(nodes, enabled)
191

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

195
        @param enabled only return enabled nodes?
196

197
        @return dpids list of dpids
198
        '''
199
        def is_host(n):
200
            '''Returns true if node is a host.'''
201
            return not self.node_info[n].is_switch
202

    
203
        nodes = [n for n in self.g.nodes() if is_host(n)]
204
        return self.nodes_enabled(nodes, enabled)
205

    
206
    def edge_enabled(self, edge):
207
        '''Is edge admin on, powered on, and fault-free?
208

209
        @param edge (src, dst) dpid tuple
210

211
        @return bool edge is enabled
212
        '''
213
        src, dst = edge
214
        src, dst = tuple(sorted([src, dst]))
215
        ei = self.edge_info[tuple(sorted([src, dst]))]
216
        return ei.admin_on and ei.power_on and not ei.fault
217

    
218
    def edges_enabled(self, edges, enabled = True):
219
        '''Return subset of enabled edges
220

221
        @param edges list of edges
222
        @param enabled only return enabled edges?
223

224
        @return edges filtered list of edges
225
        '''
226
        if enabled:
227
            return [e for e in edges if self.edge_enabled(e)]
228
        else:
229
            return edges
230

    
231
    def edges(self, enabled = True):
232
        '''Return edges.
233

234
        @param enabled only return enabled edges?
235

236
        @return edges list of dpid pairs
237
        '''
238
        return self.edges_enabled(self.g.edges(), enabled)
239

    
240
    def edges_str(self, dpid_pairs):
241
        '''Return string of custom-encoded node pairs.
242

243
        @param dpid_pairs list of dpid pairs (src, dst)
244

245
        @return str string
246
        '''
247
        edges = []
248
        for pair in dpid_pairs:
249
            src, dst = pair
250
            src = str(self.id_gen(dpid = src))
251
            dst = str(self.id_gen(dpid = dst))
252
            edges.append((src, dst))
253
        return edges
254

    
255
    def port(self, src, dst):
256
        '''Get port number.
257

258
        @param src source switch DPID
259
        @param dst destination switch DPID
260
        @return tuple (src_port, dst_port):
261
            src_port: port on source switch leading to the destination switch
262
            dst_port: port on destination switch leading to the source switch
263
        '''
264
        if src in self.ports and dst in self.ports[src]:
265
            assert dst in self.ports and src in self.ports[dst]
266
            return (self.ports[src][dst], self.ports[dst][src])
267

    
268
    def enable_edges(self):
269
        '''Enable all edges in the network graph.
270

271
        Set admin on, power on, and fault off.
272
        '''
273
        for e in self.g.edges():
274
            src, dst = e
275
            ei = self.edge_info[tuple(sorted([src, dst]))]
276
            ei.admin_on = True
277
            ei.power_on = True
278
            ei.fault = False
279

    
280
    def enable_nodes(self):
281
        '''Enable all nodes in the network graph.
282

283
        Set connected on, admin on, power on, and fault off.
284
        '''
285
        for node in self.g.nodes():
286
            ni = self.node_info[node]
287
            ni.connected = True
288
            ni.admin_on = True
289
            ni.power_on = True
290
            ni.fault = False
291

    
292
    def enable_all(self):
293
        '''Enable all nodes and edges in the network graph.'''
294
        self.enable_nodes()
295
        self.enable_edges()
296

    
297
    def name(self, dpid):
298
        '''Get string name of node ID.
299

300
        @param dpid DPID of host or switch
301
        @return name_str string name with no dashes
302
        '''
303
        return self.id_gen(dpid = dpid).name_str()
304

    
305
    def ip(self, dpid):
306
        '''Get IP dotted-decimal string of node ID.
307

308
        @param dpid DPID of host or switch
309
        @return ip_str
310
        '''
311
        return self.id_gen(dpid = dpid).ip_str()
312

    
313

    
314
class SingleSwitchTopo(Topo):
315
    '''Single switch connected to k hosts.'''
316

    
317
    def __init__(self, k = 2, enable_all = True):
318
        '''Init.
319

320
        @param k number of hosts
321
        @param enable_all enables all nodes and switches?
322
        '''
323
        super(SingleSwitchTopo, self).__init__()
324

    
325
        self.k = k
326

    
327
        self._add_node(1, Node())
328
        hosts = range(2, k + 2)
329
        for h in hosts:
330
            self._add_node(h, Node(is_switch = False))
331
            self._add_edge(h, 1, Edge())
332

    
333
        if enable_all:
334
            self.enable_all()
335

    
336

    
337
class LinearTopo(Topo):
338
    '''Linear topology of k switches, with one host per switch.'''
339

    
340
    def __init__(self, k = 2, enable_all = True):
341
        '''Init.
342

343
        @param k number of switches (and hosts too)
344
        @param enable_all enables all nodes and switches?
345
        '''
346
        super(LinearTopo, self).__init__()
347

    
348
        self.k = k
349

    
350
        switches = range(1, k + 1)
351
        for s in switches:
352
            h = s + k
353
            self._add_node(s, Node())
354
            self._add_node(h, Node(is_switch = False))
355
            self._add_edge(s, h, Edge())
356
        for s in switches:
357
            if s != k:
358
                self._add_edge(s, s + 1, Edge())
359

    
360
        if enable_all:
361
            self.enable_all()