carlosmscabral | f40ecd1 | 2013-02-01 18:15:58 -0200 | [diff] [blame] | 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 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 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 | |
| 170 | class 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 | |
| 189 | class 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 | |
| 210 | class 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 |