Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 5a8bb489

History | View | Annotate | Download (7.91 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.util import netParse, ipStr, irange, natural, naturalSeq
20

    
21
class Topo(object):
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"""
29
        self.g = Graph()
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
35
        self.ports = {}  # ports[src][dst] is port on src that connects to dst
36

    
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)
78
        self.g.add_edge(src, dst)
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
84
        self.add_port(src, dst)
85
        return src, dst
86

    
87
    def add_port(self, src, dst):
88
        '''Generate port mapping for new edge.
89

90
        @param src source switch DPID
91
        @param dst destination switch DPID
92
        '''
93
        src_base = 1 if self.is_switch(src) else 0
94
        dst_base = 1 if self.is_switch(dst) else 0
95
        if src not in self.ports:
96
            self.ports[src] = {}
97
        if dst not in self.ports[src]:
98
            # num outlinks
99
            self.ports[src][dst] = len(self.ports[src]) + src_base
100
        if dst not in self.ports:
101
            self.ports[dst] = {}
102
        if src not in self.ports[dst]:
103
            # num outlinks
104
            self.ports[dst][src] = len(self.ports[dst]) + dst_base
105
    
106
    def nodes(self, sort=True):
107
        "Return nodes in graph"
108
        if sort:
109
            return sorted( self.g.nodes(), key=natural )
110
        else:
111
            return self.g.nodes()
112

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

    
118
    def switches(self, sort=True):
119
        '''Return switches.
120
        sort: sort switches alphabetically
121
        @return dpids list of dpids
122
        '''
123
        return [n for n in self.nodes(sort) if self.is_switch(n)]
124

    
125
    def hosts(self, sort=True):
126
        '''Return hosts.
127
        sort: sort hosts alphabetically
128
        @return dpids list of dpids
129
        '''
130
        return [n for n in self.nodes(sort) if not self.is_switch(n)]
131

    
132
    def links(self, sort=True):
133
        '''Return links.
134
        sort: sort links alphabetically
135
        @return links list of name pairs
136
        '''
137
        if not sort:
138
            return self.g.edges()
139
        else:
140
            return sorted( self.g.edges(), key=naturalSeq )
141

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

145
        @param src source switch name
146
        @param dst destination switch name
147
        @return tuple (src_port, dst_port):
148
            src_port: port on source switch leading to the destination switch
149
            dst_port: port on destination switch leading to the source switch
150
        '''
151
        if src in self.ports and dst in self.ports[src]:
152
            assert dst in self.ports and src in self.ports[dst]
153
            return (self.ports[src][dst], self.ports[dst][src])
154

    
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)]
159

    
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 {}
164

    
165
    def setNodeInfo( self, name, info ):
166
        self.node_info[ name ] = info
167

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

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

    
176
    def __init__(self, k=2, **opts):
177
        '''Init.
178

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

    
184
        self.k = k
185

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

    
191

    
192
class SingleSwitchReversedTopo(SingleSwitchTopo):
193
    '''Single switch connected to k hosts, with reversed ports.
194

195
    The lowest-numbered host is connected to the highest-numbered port.
196

197
    Useful to verify that Mininet properly handles custom port numberings.
198
    '''
199

    
200
    def port(self, src, dst):
201
        '''Get port number.
202

203
        @param src source switch DPID
204
        @param dst destination switch DPID
205
        @return tuple (src_port, dst_port):
206
            src_port: port on source switch leading to the destination switch
207
            dst_port: port on destination switch leading to the source switch
208
        '''
209
        if src == 1:
210
            if dst in range(2, self.k + 2):
211
                dst_index = dst - 2
212
                highest = self.k - 1
213
                return (highest - dst_index, 0)
214
            else:
215
                raise Exception('unexpected dst: %i' % dst)
216
        elif src in range(2, self.k + 2):
217
            if dst == 1:
218
                raise Exception('unexpected dst: %i' % dst)
219
            else:
220
                src_index = src - 2
221
                highest = self.k - 1
222
                return (0, highest - src_index)
223

    
224

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

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

    
234
        super(LinearTopo, self).__init__(**opts)
235

    
236
        self.k = k
237

    
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