Statistics
| Branch: | Tag: | Revision:

mininet / mininet / topo.py @ 65c35b65

History | View | Annotate | Download (7.61 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 mininet.util import irange, natural, naturalSeq
15

    
16
class Graph(object):
17
    "Utility class to track nodes and edges "
18

    
19
    def __init__(self):
20
        self.data = {}
21

    
22
    def add_node(self,node):
23
        if node not in self.data.keys():
24
            self.data[node] = []
25

    
26
    def add_edge(self,src,dest):
27
        self.add_node(src)
28
        self.add_node(dest)
29
        self.data[src].append(dest)
30

    
31
    def nodes(self):
32
        return self.data.keys()
33

    
34
    def edges(self):
35
        for src in self.data.keys():
36
            for dest in self.data[src]:
37
                yield (src,dest)
38

    
39
class Topo(object):
40
    "Data center network representation for structured multi-trees."
41

    
42
    def __init__(self, hopts=None, sopts=None, lopts=None):
43
        """Topo object:
44
           hinfo: default host options
45
           sopts: default switch options
46
           lopts: default link options"""
47
        self.g = Graph()
48
        self.node_info = {}
49
        self.link_info = {}  # (src, dst) tuples hash to EdgeInfo objects
50
        self.hopts = {} if hopts is None else hopts
51
        self.sopts = {} if sopts is None else sopts
52
        self.lopts = {} if lopts is None else lopts
53
        self.ports = {}  # ports[src][dst] is port on src that connects to dst
54

    
55
    def addNode(self, name, **opts):
56
        """Add Node to graph.
57
           name: name
58
           opts: node options
59
           returns: node name"""
60
        self.g.add_node(name)
61
        self.node_info[name] = opts
62
        return name
63

    
64
    def addHost(self, name, **opts):
65
        """Convenience method: Add host to graph.
66
           name: host name
67
           opts: host options
68
           returns: host name"""
69
        if not opts and self.hopts:
70
            opts = self.hopts
71
        return self.addNode(name, **opts)
72

    
73
    def addSwitch(self, name, **opts):
74
        """Convenience method: Add switch to graph.
75
           name: switch name
76
           opts: switch options
77
           returns: switch name"""
78
        if not opts and self.sopts:
79
            opts = self.sopts
80
        result = self.addNode(name, isSwitch=True, **opts)
81
        return result
82

    
83
    def addLink(self, node1, node2, port1=None, port2=None,
84
                **opts):
85
        """node1, node2: nodes to link together
86
           port1, port2: ports (optional)
87
           opts: link options (optional)
88
           returns: link info key"""
89
        if not opts and self.lopts:
90
            opts = self.lopts
91
        self.addPort(node1, node2, port1, port2)
92
        key = tuple(self.sorted([node1, node2]))
93
        self.link_info[key] = opts
94
        self.g.add_edge(*key)
95
        return key
96

    
97
    def addPort(self, src, dst, sport=None, dport=None):
98
        '''Generate port mapping for new edge.
99
        @param src source switch name
100
        @param dst destination switch name
101
        '''
102
        self.ports.setdefault(src, {})
103
        self.ports.setdefault(dst, {})
104
        # New port: number of outlinks + base
105
        src_base = 1 if self.isSwitch(src) else 0
106
        dst_base = 1 if self.isSwitch(dst) else 0
107
        if sport is None:
108
            sport = len(self.ports[src]) + src_base
109
        if dport is None:
110
            dport = len(self.ports[dst]) + dst_base
111
        self.ports[src][dst] = sport
112
        self.ports[dst][src] = dport
113

    
114
    def nodes(self, sort=True):
115
        "Return nodes in graph"
116
        if sort:
117
            return self.sorted( self.g.nodes() )
118
        else:
119
            return self.g.nodes()
120

    
121
    def isSwitch(self, n):
122
        '''Returns true if node is a switch.'''
123
        info = self.node_info[n]
124
        return info and info.get('isSwitch', False)
125

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

    
133
    def hosts(self, sort=True):
134
        '''Return hosts.
135
        sort: sort hosts alphabetically
136
        @return dpids list of dpids
137
        '''
138
        return [n for n in self.nodes(sort) if not self.isSwitch(n)]
139

    
140
    def links(self, sort=True):
141
        '''Return links.
142
        sort: sort links alphabetically
143
        @return links list of name pairs
144
        '''
145
        if not sort:
146
            return self.g.edges()
147
        else:
148
            links = [tuple(self.sorted(e)) for e in self.g.edges()]
149
            return sorted( links, key=naturalSeq )
150

    
151
    def port(self, src, dst):
152
        '''Get port number.
153

154
        @param src source switch name
155
        @param dst destination switch name
156
        @return tuple (src_port, dst_port):
157
            src_port: port on source switch leading to the destination switch
158
            dst_port: port on destination switch leading to the source switch
159
        '''
160
        if src in self.ports and dst in self.ports[src]:
161
            assert dst in self.ports and src in self.ports[dst]
162
            return (self.ports[src][dst], self.ports[dst][src])
163

    
164
    def linkInfo( self, src, dst ):
165
        "Return link metadata"
166
        src, dst = self.sorted([src, dst])
167
        return self.link_info[(src, dst)]
168

    
169
    def setlinkInfo( self, src, dst, info ):
170
        "Set link metadata"
171
        src, dst = self.sorted([src, dst])
172
        self.link_info[(src, dst)] = info
173

    
174
    def nodeInfo( self, name ):
175
        "Return metadata (dict) for node"
176
        info = self.node_info[ name ]
177
        return info if info is not None else {}
178

    
179
    def setNodeInfo( self, name, info ):
180
        "Set metadata (dict) for node"
181
        self.node_info[ name ] = info
182

    
183
    @staticmethod
184
    def sorted( items ):
185
        "Items sorted in natural (i.e. alphabetical) order"
186
        return sorted(items, key=natural)
187

    
188
class SingleSwitchTopo(Topo):
189
    '''Single switch connected to k hosts.'''
190

    
191
    def __init__(self, k=2, **opts):
192
        '''Init.
193

194
        @param k number of hosts
195
        @param enable_all enables all nodes and switches?
196
        '''
197
        super(SingleSwitchTopo, self).__init__(**opts)
198

    
199
        self.k = k
200

    
201
        switch = self.addSwitch('s1')
202
        for h in irange(1, k):
203
            host = self.addHost('h%s' % h)
204
            self.addLink(host, switch)
205

    
206

    
207
class SingleSwitchReversedTopo(Topo):
208
    '''Single switch connected to k hosts, with reversed ports.
209

210
    The lowest-numbered host is connected to the highest-numbered port.
211

212
    Useful to verify that Mininet properly handles custom port numberings.
213
    '''
214
    def __init__(self, k=2, **opts):
215
        '''Init.
216

217
        @param k number of hosts
218
        @param enable_all enables all nodes and switches?
219
        '''
220
        super(SingleSwitchReversedTopo, self).__init__(**opts)
221
        self.k = k
222
        switch = self.addSwitch('s1')
223
        for h in irange(1, k):
224
            host = self.addHost('h%s' % h)
225
            self.addLink(host, switch,
226
                         port1=0, port2=(k - h + 1))
227

    
228
class LinearTopo(Topo):
229
    "Linear topology of k switches, with one host per switch."
230

    
231
    def __init__(self, k=2, **opts):
232
        """Init.
233
           k: number of switches (and hosts)
234
           hconf: host configuration options
235
           lconf: link configuration options"""
236

    
237
        super(LinearTopo, self).__init__(**opts)
238

    
239
        self.k = k
240

    
241
        lastSwitch = None
242
        for i in irange(1, k):
243
            host = self.addHost('h%s' % i)
244
            switch = self.addSwitch('s%s' % i)
245
            self.addLink( host, switch)
246
            if lastSwitch:
247
                self.addLink( switch, lastSwitch)
248
            lastSwitch = switch