blob: bd4afb530fd35979e0956fb69ae98bf140d51104 [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001#!/usr/bin/env python
2'''@package topo
3
4Network topology creation.
5
6@author Brandon Heller (brandonh@stanford.edu)
7
8This package includes code to represent network topologies.
9
10A Topo object can be a topology database for NOX, can represent a physical
11setup 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
18from networkx import Graph
19from mininet.util import irange, natural, naturalSeq
Caio188d2f32015-01-22 23:35:08 -020020import pdb
carlosmscabralf40ecd12013-02-01 18:15:58 -020021
22class Topo(object):
23 "Data center network representation for structured multi-trees."
24
Caio188d2f32015-01-22 23:35:08 -020025 def __init__(self, hopts=None, sopts=None, lopts=None, ropts=None):
carlosmscabralf40ecd12013-02-01 18:15:58 -020026 """Topo object:
27 hinfo: default host options
28 sopts: default switch options
29 lopts: default link options"""
30 self.g = Graph()
31 self.node_info = {}
32 self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
33 self.hopts = {} if hopts is None else hopts
Caio188d2f32015-01-22 23:35:08 -020034 self.ropts = {} if ropts is None else ropts
carlosmscabralf40ecd12013-02-01 18:15:58 -020035 self.sopts = {} if sopts is None else sopts
36 self.lopts = {} if lopts is None else lopts
37 self.ports = {} # ports[src][dst] is port on src that connects to dst
38
39 def addNode(self, name, **opts):
40 """Add Node to graph.
41 name: name
42 opts: node options
43 returns: node name"""
44 self.g.add_node(name)
45 self.node_info[name] = opts
46 return name
47
48 def addHost(self, name, **opts):
49 """Convenience method: Add host to graph.
50 name: host name
51 opts: host options
52 returns: host name"""
Caio188d2f32015-01-22 23:35:08 -020053 #pdb.set_trace()
54 if not opts:
55 if self.hopts:
carlosmscabralf40ecd12013-02-01 18:15:58 -020056 opts = self.hopts
Caio188d2f32015-01-22 23:35:08 -020057 elif self.ropts:
58 opts = self.ropts
carlosmscabralf40ecd12013-02-01 18:15:58 -020059 return self.addNode(name, **opts)
60
61 def addSwitch(self, name, **opts):
62 """Convenience method: Add switch to graph.
63 name: switch name
64 opts: switch options
65 returns: switch name"""
66 if not opts and self.sopts:
67 opts = self.sopts
68 result = self.addNode(name, isSwitch=True, **opts)
69 return result
70
71 def addLink(self, node1, node2, port1=None, port2=None,
72 **opts):
73 """node1, node2: nodes to link together
74 port1, port2: ports (optional)
75 opts: link options (optional)
76 returns: link info key"""
77 if not opts and self.lopts:
78 opts = self.lopts
79 self.addPort(node1, node2, port1, port2)
80 key = tuple(self.sorted([node1, node2]))
81 self.link_info[key] = opts
82 self.g.add_edge(*key)
83 return key
84
85 def addPort(self, src, dst, sport=None, dport=None):
86 '''Generate port mapping for new edge.
87 @param src source switch name
88 @param dst destination switch name
89 '''
90 self.ports.setdefault(src, {})
91 self.ports.setdefault(dst, {})
92 # New port: number of outlinks + base
93 src_base = 1 if self.isSwitch(src) else 0
94 dst_base = 1 if self.isSwitch(dst) else 0
95 if sport is None:
96 sport = len(self.ports[src]) + src_base
97 if dport is None:
98 dport = len(self.ports[dst]) + dst_base
99 self.ports[src][dst] = sport
100 self.ports[dst][src] = dport
101
102 def nodes(self, sort=True):
103 "Return nodes in graph"
104 if sort:
105 return self.sorted( self.g.nodes() )
106 else:
107 return self.g.nodes()
108
109 def isSwitch(self, n):
110 '''Returns true if node is a switch.'''
Caio188d2f32015-01-22 23:35:08 -0200111 #pdb.set_trace()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200112 info = self.node_info[n]
113 return info and info.get('isSwitch', False)
114
115 def switches(self, sort=True):
116 '''Return switches.
117 sort: sort switches alphabetically
118 @return dpids list of dpids
119 '''
120 return [n for n in self.nodes(sort) if self.isSwitch(n)]
121
122 def hosts(self, sort=True):
123 '''Return hosts.
124 sort: sort hosts alphabetically
125 @return dpids list of dpids
126 '''
127 return [n for n in self.nodes(sort) if not self.isSwitch(n)]
128
129 def links(self, sort=True):
130 '''Return links.
131 sort: sort links alphabetically
132 @return links list of name pairs
133 '''
134 if not sort:
135 return self.g.edges()
136 else:
137 links = [tuple(self.sorted(e)) for e in self.g.edges()]
138 return sorted( links, key=naturalSeq )
139
140 def port(self, src, dst):
141 '''Get port number.
142
143 @param src source switch name
144 @param dst destination switch name
145 @return tuple (src_port, dst_port):
146 src_port: port on source switch leading to the destination switch
147 dst_port: port on destination switch leading to the source switch
148 '''
149 if src in self.ports and dst in self.ports[src]:
150 assert dst in self.ports and src in self.ports[dst]
151 return (self.ports[src][dst], self.ports[dst][src])
152
153 def linkInfo( self, src, dst ):
154 "Return link metadata"
155 src, dst = self.sorted([src, dst])
156 return self.link_info[(src, dst)]
157
158 def setlinkInfo( self, src, dst, info ):
159 "Set link metadata"
160 src, dst = self.sorted([src, dst])
161 self.link_info[(src, dst)] = info
162
163 def nodeInfo( self, name ):
164 "Return metadata (dict) for node"
165 info = self.node_info[ name ]
166 return info if info is not None else {}
167
168 def setNodeInfo( self, name, info ):
169 "Set metadata (dict) for node"
170 self.node_info[ name ] = info
171
172 @staticmethod
173 def sorted( items ):
174 "Items sorted in natural (i.e. alphabetical) order"
175 return sorted(items, key=natural)
176
177class SingleSwitchTopo(Topo):
178 '''Single switch connected to k hosts.'''
179
180 def __init__(self, k=2, **opts):
181 '''Init.
182
183 @param k number of hosts
184 @param enable_all enables all nodes and switches?
185 '''
186 super(SingleSwitchTopo, self).__init__(**opts)
187
188 self.k = k
189
190 switch = self.addSwitch('s1')
191 for h in irange(1, k):
192 host = self.addHost('h%s' % h)
193 self.addLink(host, switch)
194
195
196class SingleSwitchReversedTopo(Topo):
197 '''Single switch connected to k hosts, with reversed ports.
198
199 The lowest-numbered host is connected to the highest-numbered port.
200
201 Useful to verify that Mininet properly handles custom port numberings.
202 '''
203 def __init__(self, k=2, **opts):
204 '''Init.
205
206 @param k number of hosts
207 @param enable_all enables all nodes and switches?
208 '''
209 super(SingleSwitchReversedTopo, self).__init__(**opts)
210 self.k = k
211 switch = self.addSwitch('s1')
212 for h in irange(1, k):
213 host = self.addHost('h%s' % h)
214 self.addLink(host, switch,
215 port1=0, port2=(k - h + 1))
216
217class LinearTopo(Topo):
218 "Linear topology of k switches, with one host per switch."
219
220 def __init__(self, k=2, **opts):
221 """Init.
222 k: number of switches (and hosts)
223 hconf: host configuration options
224 lconf: link configuration options"""
225
226 super(LinearTopo, self).__init__(**opts)
227
228 self.k = k
229
230 lastSwitch = None
231 for i in irange(1, k):
232 host = self.addHost('h%s' % i)
233 switch = self.addSwitch('s%s' % i)
234 self.addLink( host, switch)
235 if lastSwitch:
236 self.addLink( switch, lastSwitch)
237 lastSwitch = switch