Revision 5a8bb489 mininet/topo.py

View differences:

mininet/topo.py
16 16
# from networkx.classes.graph import Graph
17 17

  
18 18
from networkx import Graph
19
from mininet.util import netParse, ipStr
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, ipBase=None, prefixLen=8, ipBaseNum=0x0a000000):
47
        '''Name conversion.
48
        ipBase: optional base IP address string
49
        prefixLen: optional IP prefix length
50
        ipBaseNum: option base IP address as int
51
        @return ip ip as string
52
        '''
53
        if ipBase:
54
            ipnum, prefixLen = netParse( ipBase )
55
        else:
56
            ipBaseNum = ipBaseNum
57
        # Ugly but functional
58
        assert self.dpid < ( 1 << ( 32 - prefixLen ) )
59
        mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
60
        ipnum = self.dpid + ( ipBaseNum & mask )
61
        return ipStr( ipnum )
62

  
63

  
64
class Node( object ):
65
    '''Node-specific vertex metadata for a Topo object.'''
66

  
67
    def __init__(self, connected=False, admin_on=True,
68
                 power_on=True, fault=False, is_switch=True,
69
                 cls=None, **params ):
70
        '''Init.
71

  
72
        @param connected actively connected to controller
73
        @param admin_on administratively on or off
74
        @param power_on powered on or off
75
        @param fault fault seen on node
76
        @param is_switch switch or host
77
        @param cls node class (e.g. Host, Switch)
78
        @param params node parameters
79
        '''
80
        self.connected = connected
81
        self.admin_on = admin_on
82
        self.power_on = power_on
83
        self.fault = fault
84
        self.is_switch = is_switch
85
        # BL: Above should mostly be deleted and replaced by the following
86
        # is_switch is a bit annoying if we are already specifying
87
        # a switch class!! Except that if cls is not specified,
88
        # then Mininet() knows whether to create a switch or a host
89
        # node and can call its own constructors...
90
        self.cls = cls
91
        self.params = params
92

  
93

  
94
class Edge(object):
95
    '''Edge-specific metadata for a StructuredTopo graph.'''
96

  
97
    def __init__(self, admin_on=True, power_on=True, fault=False,
98
                 cls=None, **params):
99
        '''Init.
100

  
101
        @param admin_on administratively on or off; defaults to True
102
        @param power_on powered on or off; defaults to True
103
        @param fault fault seen on edge; defaults to False
104
        '''
105
        self.admin_on = admin_on
106
        self.power_on = power_on
107
        self.fault = fault
108
        # Above should be deleted and replaced by the following
109
        self.cls = cls
110
        self.params = params
111

  
19
from mininet.util import netParse, ipStr, irange, natural, naturalSeq
112 20

  
113 21
class Topo(object):
114
    """Data center network representation for structured multi-trees.
115
       Note that the order of precedence is:
116
       per-node/link classes and parameters
117
       per-topo classes
118
       per-network classes"""
119

  
120
    def __init__(self, node=None, switch=None, link=None ):
121
        """Create Topo object.
122
           node: default node/host class (optional)
123
           switch: default switch class (optional)
124
           link: default link class (optional)
125
           ipBase: default IP address base (optional)"""
22
    "Data center network representation for structured multi-trees."
23

  
24
    def __init__(self, hopts=None, sopts=None, lopts=None):
25
        """Topo object:
26
           hinfo: default host options
27
           sopts: default switch options
28
           lopts: default link options"""
126 29
        self.g = Graph()
127
        self.node_info = {}  # dpids hash to Node objects
128
        self.edge_info = {}  # (src_dpid, dst_dpid) tuples hash to Edge objects
30
        self.node_info = {}
31
        self.link_info = {}  # (src, dst) tuples hash to EdgeInfo objects
32
        self.hopts = {} if hopts is None else hopts
33
        self.sopts = {} if sopts is None else lopts
34
        self.lopts = {} if lopts is None else lopts
129 35
        self.ports = {}  # ports[src][dst] is port on src that connects to dst
130
        self.id_gen = NodeID  # class used to generate dpid
131
        self.node = node
132
        self.switch = switch
133
        self.link = link
134

  
135
    def add_node(self, dpid, node=None):
136
        '''Add Node to graph.
137 36

  
138
        @param dpid dpid
139
        @param node Node object
140
        '''
141
        self.g.add_node(dpid)
142
        if not node:
143
            node = Node( link=self.link )
144
        self.node_info[dpid] = node
145

  
146
    def add_edge(self, src, dst, edge=None):
147
        '''Add edge (Node, Node) to graph.
148

  
149
        @param src src dpid
150
        @param dst dst dpid
151
        @param edge Edge object
152
        '''
153
        src, dst = tuple(sorted([src, dst]))
37
    def add_node(self, name, *args, **opts):
38
        """Add Node to graph.
39
           add_node('name', dict) <or> add_node('name', **opts)
40
           name: name
41
           args: dict of node options 
42
           opts: node options"""
43
        self.g.add_node(name)
44
        if args and type(args[0]) is dict:
45
            opts = args[0]
46
        self.node_info[name] = opts
47
        return name
48

  
49
    def add_host(self, name, *args, **opts):
50
        """Convenience method: Add host to graph.
51
           add_host('name', dict) <or> add_host('name', **opts)
52
           name: name
53
           args: dict of node options 
54
           opts: node options"""
55
        if not opts and self.hopts:
56
            opts = self.hopts
57
        return self.add_node(name, *args, **opts)
58

  
59
    def add_switch(self, name, **opts):
60
        """Convenience method: Add switch to graph.
61
           add_switch('name', dict) <or> add_switch('name', **opts)
62
           name: name
63
           args: dict of node options 
64
           opts: node options"""
65
        if not opts and self.sopts:
66
            opts = self.sopts
67
        result = self.add_node(name, is_switch=True, **opts)
68
        return result
69

  
70
    def add_link(self, src, dst, *args, **opts):
71
        """Add link (Node, Node) to topo.
72
           add_link(src, dst, dict) <or> add_link(src, dst, **opts)
73
           src: src name
74
           dst: dst name
75
           args: dict of node options
76
           params: link parameters"""
77
        src, dst = sorted([src, dst], key=naturalSeq)
154 78
        self.g.add_edge(src, dst)
155
        if not edge:
156
            edge = Edge( cls=self.link )
157
        self.edge_info[(src, dst)] = edge
79
        if args and type(args[0]) is dict:
80
            opts = args[0]
81
        if not opts and self.sopts:
82
            opts = self.sopts
83
        self.link_info[(src, dst)] = opts
158 84
        self.add_port(src, dst)
85
        return src, dst
159 86

  
160 87
    def add_port(self, src, dst):
161 88
        '''Generate port mapping for new edge.
......
175 102
        if src not in self.ports[dst]:
176 103
            # num outlinks
177 104
            self.ports[dst][src] = len(self.ports[dst]) + dst_base
178

  
179
    def node_enabled(self, dpid):
180
        '''Is node connected, admin on, powered on, and fault-free?
181

  
182
        @param dpid dpid
183

  
184
        @return bool node is enabled
185
        '''
186
        ni = self.node_info[dpid]
187
        return ni.connected and ni.admin_on and ni.power_on and not ni.fault
188

  
189
    def nodes_enabled(self, dpids, enabled = True):
190
        '''Return subset of enabled nodes
191

  
192
        @param dpids list of dpids
193
        @param enabled only return enabled nodes?
194

  
195
        @return dpids filtered list of dpids
196
        '''
197
        if enabled:
198
            return [n for n in dpids if self.node_enabled(n)]
105
    
106
    def nodes(self, sort=True):
107
        "Return nodes in graph"
108
        if sort:
109
            return sorted( self.g.nodes(), key=natural )
199 110
        else:
200
            return dpids
201

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

  
205
        @param enabled only return enabled nodes?
206

  
207
        @return dpids list of dpids
208
        '''
209
        return self.nodes_enabled(self.g.nodes(), enabled)
210

  
211
    def nodes_str(self, dpids):
212
        '''Return string of custom-encoded nodes.
213

  
214
        @param dpids list of dpids
215

  
216
        @return str string
217
        '''
218
        return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
111
            return self.g.nodes()
219 112

  
220 113
    def is_switch(self, n):
221 114
        '''Returns true if node is a switch.'''
222
        return self.node_info[n].is_switch
115
        info = self.node_info[n]
116
        return info and info['is_switch']
223 117

  
224
    def switches(self, enabled = True):
118
    def switches(self, sort=True):
225 119
        '''Return switches.
226

  
227
        @param enabled only return enabled nodes?
228

  
120
        sort: sort switches alphabetically
229 121
        @return dpids list of dpids
230 122
        '''
231
        nodes = [n for n in self.g.nodes() if self.is_switch(n)]
232
        return self.nodes_enabled(nodes, enabled)
123
        return [n for n in self.nodes(sort) if self.is_switch(n)]
233 124

  
234
    def hosts(self, enabled = True):
125
    def hosts(self, sort=True):
235 126
        '''Return hosts.
236

  
237
        @param enabled only return enabled nodes?
238

  
127
        sort: sort hosts alphabetically
239 128
        @return dpids list of dpids
240 129
        '''
130
        return [n for n in self.nodes(sort) if not self.is_switch(n)]
241 131

  
242
        def is_host(n):
243
            '''Returns true if node is a host.'''
244
            return not self.node_info[n].is_switch
245

  
246
        nodes = [n for n in self.g.nodes() if is_host(n)]
247
        return self.nodes_enabled(nodes, enabled)
248

  
249
    def edge_enabled(self, edge):
250
        '''Is edge admin on, powered on, and fault-free?
251

  
252
        @param edge (src, dst) dpid tuple
253

  
254
        @return bool edge is enabled
255
        '''
256
        src, dst = edge
257
        src, dst = tuple(sorted([src, dst]))
258
        ei = self.edge_info[tuple(sorted([src, dst]))]
259
        return ei.admin_on and ei.power_on and not ei.fault
260

  
261
    def edges_enabled(self, edges, enabled = True):
262
        '''Return subset of enabled edges
263

  
264
        @param edges list of edges
265
        @param enabled only return enabled edges?
266

  
267
        @return edges filtered list of edges
132
    def links(self, sort=True):
133
        '''Return links.
134
        sort: sort links alphabetically
135
        @return links list of name pairs
268 136
        '''
269
        if enabled:
270
            return [e for e in edges if self.edge_enabled(e)]
137
        if not sort:
138
            return self.g.edges()
271 139
        else:
272
            return edges
273

  
274
    def edges(self, enabled = True):
275
        '''Return edges.
276

  
277
        @param enabled only return enabled edges?
278

  
279
        @return edges list of dpid pairs
280
        '''
281
        return self.edges_enabled(self.g.edges(), enabled)
282

  
283
    def edges_str(self, dpid_pairs):
284
        '''Return string of custom-encoded node pairs.
285

  
286
        @param dpid_pairs list of dpid pairs (src, dst)
287

  
288
        @return str string
289
        '''
290
        edges = []
291
        for pair in dpid_pairs:
292
            src, dst = pair
293
            src = str(self.id_gen(dpid = src))
294
            dst = str(self.id_gen(dpid = dst))
295
            edges.append((src, dst))
296
        return edges
140
            return sorted( self.g.edges(), key=naturalSeq )
297 141

  
298 142
    def port(self, src, dst):
299 143
        '''Get port number.
300 144

  
301
        @param src source switch DPID
302
        @param dst destination switch DPID
145
        @param src source switch name
146
        @param dst destination switch name
303 147
        @return tuple (src_port, dst_port):
304 148
            src_port: port on source switch leading to the destination switch
305 149
            dst_port: port on destination switch leading to the source switch
......
308 152
            assert dst in self.ports and src in self.ports[dst]
309 153
            return (self.ports[src][dst], self.ports[dst][src])
310 154

  
311
    def edgeInfo( self, src, dst ):
312
        "Return edge metadata"
313
        # BL: Perhaps this should be rethought or we should just use the
314
        # dicts...
315
        return self.edge_info[ ( src, dst ) ]
316

  
317
    def enable_edges(self):
318
        '''Enable all edges in the network graph.
319

  
320
        Set admin on, power on, and fault off.
321
        '''
322
        for e in self.g.edges():
323
            src, dst = e
324
            ei = self.edge_info[tuple(sorted([src, dst]))]
325
            ei.admin_on = True
326
            ei.power_on = True
327
            ei.fault = False
328

  
329
    def enable_nodes(self):
330
        '''Enable all nodes in the network graph.
331

  
332
        Set connected on, admin on, power on, and fault off.
333
        '''
334
        for node in self.g.nodes():
335
            ni = self.node_info[node]
336
            ni.connected = True
337
            ni.admin_on = True
338
            ni.power_on = True
339
            ni.fault = False
340

  
341
    def enable_all(self):
342
        '''Enable all nodes and edges in the network graph.'''
343
        self.enable_nodes()
344
        self.enable_edges()
345

  
346
    def name(self, dpid):
347
        '''Get string name of node ID.
348

  
349
        @param dpid DPID of host or switch
350
        @return name_str string name with no dashes
351
        '''
352
        return self.id_gen(dpid = dpid).name_str()
155
    def linkInfo( self, src, dst ):
156
        "Return link metadata"
157
        src, dst = sorted((src, dst), key=naturalSeq)
158
        return self.link_info[(src, dst)]
353 159

  
354
    def ip(self, dpid, **params):
355
        '''Get IP dotted-decimal string of node ID.
356
        @param dpid DPID of host or switch
357
        @param params: params to pass to ip_str
358
        @return ip_str
359
        '''
360
        return self.id_gen(dpid = dpid).ip_str(**params)
160
    def nodeInfo( self, name ):
161
        "Return metadata (dict) for node"
162
        info = self.node_info[ name ]
163
        return info if info is not None else {}
361 164

  
362
    def nodeInfo( self, dpid ):
363
        "Return metadata for node"
364
        # BL: may wish to rethink this or just use dicts..
365
        return self.node_info[ dpid ]
165
    def setNodeInfo( self, name, info ):
166
        self.node_info[ name ] = info
366 167

  
168
    @staticmethod
169
    def sorted( items ):
170
        "Items sorted in natural (i.e. alphabetical) order"
171
        return sorted(items, key=natural)
367 172

  
368 173
class SingleSwitchTopo(Topo):
369 174
    '''Single switch connected to k hosts.'''
370 175

  
371
    def __init__(self, k = 2, enable_all = True):
176
    def __init__(self, k=2, **opts):
372 177
        '''Init.
373 178

  
374 179
        @param k number of hosts
375 180
        @param enable_all enables all nodes and switches?
376 181
        '''
377
        super(SingleSwitchTopo, self).__init__()
182
        super(SingleSwitchTopo, self).__init__(**opts)
378 183

  
379 184
        self.k = k
380 185

  
381
        self.add_node(1, Node())
382
        hosts = range(2, k + 2)
383
        for h in hosts:
384
            self.add_node(h, Node(is_switch = False))
385
            self.add_edge(h, 1, Edge())
386

  
387
        if enable_all:
388
            self.enable_all()
186
        switch = self.add_switch('s1')
187
        for h in irange(1, k):
188
            host = self.add_host('h%s' % h)
189
            self.add_link(host, switch)
389 190

  
390 191

  
391 192
class SingleSwitchReversedTopo(SingleSwitchTopo):
......
422 223

  
423 224

  
424 225
class LinearTopo(Topo):
425
    '''Linear topology of k switches, with one host per switch.'''
226
    "Linear topology of k switches, with one host per switch."
426 227

  
427
    def __init__(self, k = 2, enable_all = True):
428
        '''Init.
228
    def __init__(self, k=2, **opts):
229
        """Init.
230
           k: number of switches (and hosts)
231
           hconf: host configuration options
232
           lconf: link configuration options"""
429 233

  
430
        @param k number of switches (and hosts too)
431
        @param enable_all enables all nodes and switches?
432
        '''
433
        super(LinearTopo, self).__init__()
234
        super(LinearTopo, self).__init__(**opts)
434 235

  
435 236
        self.k = k
436 237

  
437
        switches = range(1, k + 1)
438
        for s in switches:
439
            h = s + k
440
            self.add_node(s, Node())
441
            self.add_node(h, Node(is_switch = False))
442
            self.add_edge(s, h, Edge())
443
        for s in switches:
444
            if s != k:
445
                self.add_edge(s, s + 1, Edge())
446

  
447
        if enable_all:
448
            self.enable_all()
238
        lastSwitch = None
239
        for i in irange(1, k):
240
            host = self.add_host('h%s' % i)
241
            switch = self.add_switch('s%s' % i)
242
            self.add_link( host, switch)
243
            if lastSwitch:
244
                self.add_link( switch, lastSwitch)
245
            lastSwitch = switch

Also available in: Unified diff