blob: fff9604f00421ce7932dd26e2f2ac5ddcafd7b54 [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
20
21class 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 sopts
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 addNode(self, name, **opts):
38 """Add Node to graph.
39 name: name
40 opts: node options
41 returns: node name"""
42 self.g.add_node(name)
43 self.node_info[name] = opts
44 return name
45
46 def addHost(self, name, **opts):
47 """Convenience method: Add host to graph.
48 name: host name
49 opts: host options
50 returns: host name"""
51 if not opts and self.hopts:
52 opts = self.hopts
53 return self.addNode(name, **opts)
54
55 def addSwitch(self, name, **opts):
56 """Convenience method: Add switch to graph.
57 name: switch name
58 opts: switch options
59 returns: switch name"""
60 if not opts and self.sopts:
61 opts = self.sopts
62 result = self.addNode(name, isSwitch=True, **opts)
63 return result
64
65 def addLink(self, node1, node2, port1=None, port2=None,
66 **opts):
67 """node1, node2: nodes to link together
68 port1, port2: ports (optional)
69 opts: link options (optional)
70 returns: link info key"""
71 if not opts and self.lopts:
72 opts = self.lopts
73 self.addPort(node1, node2, port1, port2)
74 key = tuple(self.sorted([node1, node2]))
75 self.link_info[key] = opts
76 self.g.add_edge(*key)
77 return key
78
79 def addPort(self, src, dst, sport=None, dport=None):
80 '''Generate port mapping for new edge.
81 @param src source switch name
82 @param dst destination switch name
83 '''
84 self.ports.setdefault(src, {})
85 self.ports.setdefault(dst, {})
86 # New port: number of outlinks + base
87 src_base = 1 if self.isSwitch(src) else 0
88 dst_base = 1 if self.isSwitch(dst) else 0
89 if sport is None:
90 sport = len(self.ports[src]) + src_base
91 if dport is None:
92 dport = len(self.ports[dst]) + dst_base
93 self.ports[src][dst] = sport
94 self.ports[dst][src] = dport
95
96 def nodes(self, sort=True):
97 "Return nodes in graph"
98 if sort:
99 return self.sorted( self.g.nodes() )
100 else:
101 return self.g.nodes()
102
103 def isSwitch(self, n):
104 '''Returns true if node is a switch.'''
105 info = self.node_info[n]
106 return info and info.get('isSwitch', False)
107
108 def switches(self, sort=True):
109 '''Return switches.
110 sort: sort switches alphabetically
111 @return dpids list of dpids
112 '''
113 return [n for n in self.nodes(sort) if self.isSwitch(n)]
114
115 def hosts(self, sort=True):
116 '''Return hosts.
117 sort: sort hosts alphabetically
118 @return dpids list of dpids
119 '''
120 return [n for n in self.nodes(sort) if not self.isSwitch(n)]
121
122 def links(self, sort=True):
123 '''Return links.
124 sort: sort links alphabetically
125 @return links list of name pairs
126 '''
127 if not sort:
128 return self.g.edges()
129 else:
130 links = [tuple(self.sorted(e)) for e in self.g.edges()]
131 return sorted( links, key=naturalSeq )
132
133 def port(self, src, dst):
134 '''Get port number.
135
136 @param src source switch name
137 @param dst destination switch name
138 @return tuple (src_port, dst_port):
139 src_port: port on source switch leading to the destination switch
140 dst_port: port on destination switch leading to the source switch
141 '''
142 if src in self.ports and dst in self.ports[src]:
143 assert dst in self.ports and src in self.ports[dst]
144 return (self.ports[src][dst], self.ports[dst][src])
145
146 def linkInfo( self, src, dst ):
147 "Return link metadata"
148 src, dst = self.sorted([src, dst])
149 return self.link_info[(src, dst)]
150
151 def setlinkInfo( self, src, dst, info ):
152 "Set link metadata"
153 src, dst = self.sorted([src, dst])
154 self.link_info[(src, dst)] = info
155
156 def nodeInfo( self, name ):
157 "Return metadata (dict) for node"
158 info = self.node_info[ name ]
159 return info if info is not None else {}
160
161 def setNodeInfo( self, name, info ):
162 "Set metadata (dict) for node"
163 self.node_info[ name ] = info
164
165 @staticmethod
166 def sorted( items ):
167 "Items sorted in natural (i.e. alphabetical) order"
168 return sorted(items, key=natural)
169
170class SingleSwitchTopo(Topo):
171 '''Single switch connected to k hosts.'''
172
173 def __init__(self, k=2, **opts):
174 '''Init.
175
176 @param k number of hosts
177 @param enable_all enables all nodes and switches?
178 '''
179 super(SingleSwitchTopo, self).__init__(**opts)
180
181 self.k = k
182
183 switch = self.addSwitch('s1')
184 for h in irange(1, k):
185 host = self.addHost('h%s' % h)
186 self.addLink(host, switch)
187
188
189class SingleSwitchReversedTopo(Topo):
190 '''Single switch connected to k hosts, with reversed ports.
191
192 The lowest-numbered host is connected to the highest-numbered port.
193
194 Useful to verify that Mininet properly handles custom port numberings.
195 '''
196 def __init__(self, k=2, **opts):
197 '''Init.
198
199 @param k number of hosts
200 @param enable_all enables all nodes and switches?
201 '''
202 super(SingleSwitchReversedTopo, self).__init__(**opts)
203 self.k = k
204 switch = self.addSwitch('s1')
205 for h in irange(1, k):
206 host = self.addHost('h%s' % h)
207 self.addLink(host, switch,
208 port1=0, port2=(k - h + 1))
209
210class LinearTopo(Topo):
211 "Linear topology of k switches, with one host per switch."
212
213 def __init__(self, k=2, **opts):
214 """Init.
215 k: number of switches (and hosts)
216 hconf: host configuration options
217 lconf: link configuration options"""
218
219 super(LinearTopo, self).__init__(**opts)
220
221 self.k = k
222
223 lastSwitch = None
224 for i in irange(1, k):
225 host = self.addHost('h%s' % i)
226 switch = self.addSwitch('s%s' % i)
227 self.addLink( host, switch)
228 if lastSwitch:
229 self.addLink( switch, lastSwitch)
230 lastSwitch = switch