blob: 761189c7a2693b34914a5bff3093352fccedd43f [file] [log] [blame]
Pavlin Radoslavov15954d42013-10-19 15:29:04 -07001package net.onrc.onos.ofcontroller.topology;
2
Pavlin Radoslavovcfe7b402013-10-30 19:44:22 -07003import java.util.List;
4import java.util.LinkedList;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -07005import java.util.Map;
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -07006import java.util.TreeMap;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -07007
8import net.onrc.onos.graph.GraphDBOperation;
9import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
10import net.onrc.onos.ofcontroller.core.ISwitchStorage.SwitchState;
11
12import org.openflow.util.HexString;
13
14import com.tinkerpop.blueprints.Direction;
15import com.tinkerpop.blueprints.Vertex;
yoshitomob292c622013-11-23 14:35:58 -080016import net.onrc.onos.graph.DBOperation;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070017
18/**
19 * A class for storing Node and Link information for fast computation
20 * of shortest paths.
21 */
22class Node {
23 /**
24 * A class for storing Link information for fast computation of shortest
25 * paths.
26 */
27 class Link {
28 public Node me; // The node this link originates from
29 public Node neighbor; // The neighbor node on the other side
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070030 public int myPort; // Local port ID for the link
31 public int neighborPort; // Neighbor port ID for the link
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070032
33 /**
34 * Link constructor.
35 *
36 * @param me the node this link originates from.
37 * @param the neighbor node on the other side of the link.
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070038 * @param myPort local port ID for the link.
39 * @param neighborPort neighbor port ID for the link.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070040 */
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070041 public Link(Node me, Node neighbor, int myPort, int neighborPort) {
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070042 this.me = me;
43 this.neighbor = neighbor;
44 this.myPort = myPort;
45 this.neighborPort = neighborPort;
46 }
47 };
48
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070049 public long nodeId; // The node ID
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070050 public TreeMap<Integer, Link> links; // The links from this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070051 // (src PortID -> Link)
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070052 private TreeMap<Integer, Link> reverseLinksMap; // The links to this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070053 // (dst PortID -> Link)
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070054 private TreeMap<Integer, Integer> portsMap; // The ports on this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070055 // (PortID -> PortID)
56 // TODO: In the future will be:
57 // (PortID -> Port)
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070058
59 /**
60 * Node constructor.
61 *
62 * @param nodeId the node ID.
63 */
64 public Node(long nodeId) {
65 this.nodeId = nodeId;
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070066 links = new TreeMap<Integer, Link>();
67 reverseLinksMap = new TreeMap<Integer, Link>();
68 portsMap = new TreeMap<Integer, Integer>();
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070069 }
70
71 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070072 * Get all ports.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070073 *
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070074 * @return all ports.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070075 */
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070076 public Map<Integer, Integer> ports() {
77 return portsMap;
78 }
79
80 /**
81 * Get the port for a given Port ID.
82 *
83 * Note: For now the port itself is just the Port ID. In the future
84 * it might contain more information.
85 *
86 * @return the port if found, otherwise null.
87 */
88 public Integer getPort(int portId) {
Yuta HIGUCHIdbc8c442013-10-31 10:15:37 -070089 return portsMap.get(portId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070090 }
91
92 /**
93 * Add a port for a given Port ID.
94 *
95 * Note: For now the port itself is just the Port ID. In the future
96 * it might contain more information.
97 *
98 * @param portId the Port ID of the port to add.
99 * @return the added Port.
100 */
101 Integer addPort(int portId) {
102 Integer port = new Integer(portId);
103 portsMap.put(portId, port);
104 return port;
105 }
106
107 /**
108 * Remove a port for a given Port ID.
109 *
110 * NOTE: The outgoing and incoming links using this port are removed as
111 * well.
112 */
113 void removePort(int portId) {
114 // Remove the outgoing link
115 Link link = getLink(portId);
116 if (link != null) {
117 link.neighbor.removeReverseLink(link);
118 removeLink(portId);
119 }
120
121 // Remove the incoming link
122 Link reverseLink = reverseLinksMap.get(portId);
123 if (reverseLink != null) {
124 // NOTE: reverseLink.myPort is the neighbor's outgoing port
Pavlin Radoslavov823f3222013-11-04 21:57:31 -0800125 reverseLink.me.removeLink(reverseLink.myPort);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700126 removeReverseLink(reverseLink);
127 }
128
129 portsMap.remove(portId);
130 }
Yuta HIGUCHI63ee5a82013-10-31 10:16:15 -0700131
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700132 /**
133 * Get a link on a port to a neighbor.
134 *
135 * @param myPortId the local port ID for the link to the neighbor.
136 * @return the link if found, otherwise null.
137 */
138 public Link getLink(int myPortId) {
139 return links.get(myPortId);
140 }
141
142 /**
143 * Add a link to a neighbor.
144 *
145 * @param myPortId the local port ID for the link to the neighbor.
146 * @param neighbor the neighbor for the link.
147 * @param neighborPortId the neighbor port ID for the link.
148 * @return the added Link.
149 */
150 public Link addLink(int myPortId, Node neighbor, int neighborPortId) {
151 Link link = new Link(this, neighbor, myPortId, neighborPortId);
152 links.put(myPortId, link);
153 neighbor.addReverseLink(link);
154 return link;
155 }
156
157 /**
158 * Add a reverse link from a neighbor.
159 *
160 * @param link the reverse link from a neighbor to add.
161 */
162 private void addReverseLink(Link link) {
163 // NOTE: link.neghborPort is my port
164 reverseLinksMap.put(link.neighborPort, link);
165 }
166
167 /**
168 * Remove a link to a neighbor.
169 *
170 * @param myPortId the local port ID for the link to the neighbor.
171 */
172 public void removeLink(int myPortId) {
173 links.remove(myPortId);
174 }
175
176 /**
177 * Remove a reverse link from a neighbor.
178 *
179 * @param link the reverse link from a neighbor to remove.
180 */
181 private void removeReverseLink(Link link) {
182 // NOTE: link.neghborPort is my port
183 reverseLinksMap.remove(link.neighborPort);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700184 }
185};
186
187/**
188 * A class for storing topology information.
189 */
190public class Topology {
191 private Map<Long, Node> nodesMap; // The dpid->Node mapping
192
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700193 /**
194 * Default constructor.
195 */
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700196 public Topology() {
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -0700197 nodesMap = new TreeMap<Long, Node>();
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700198 }
199
200 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700201 * Add a topology element to the topology.
202 *
203 * @param topologyElement the topology element to add.
204 * @return true if the topology was modified, otherwise false.
205 */
206 public boolean addTopologyElement(TopologyElement topologyElement) {
207 boolean isModified = false;
208
209 switch (topologyElement.getType()) {
210 case ELEMENT_SWITCH: {
211 // Add the switch
212 Node node = getNode(topologyElement.getSwitch());
213 if (node == null) {
214 node = addNode(topologyElement.getSwitch());
215 isModified = true;
216 }
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700217 break;
218 }
219 case ELEMENT_PORT: {
220 // Add the switch
221 Node node = getNode(topologyElement.getSwitch());
222 if (node == null) {
223 node = addNode(topologyElement.getSwitch());
224 isModified = true;
225 }
226 // Add the port for the switch
227 Integer port = node.getPort(topologyElement.getSwitchPort());
228 if (port == null) {
229 node.addPort(topologyElement.getSwitchPort());
230 isModified = true;
231 }
232 break;
233 }
234 case ELEMENT_LINK: {
235 // Add the "from" switch
236 Node fromNode = getNode(topologyElement.getFromSwitch());
237 if (fromNode == null) {
238 fromNode = addNode(topologyElement.getFromSwitch());
239 isModified = true;
240 }
241 // Add the "to" switch
242 Node toNode = getNode(topologyElement.getToSwitch());
243 if (toNode == null) {
244 toNode = addNode(topologyElement.getToSwitch());
245 isModified = true;
246 }
247 // Add the "from" port
248 Integer fromPort = fromNode.getPort(topologyElement.getFromPort());
249 if (fromPort == null) {
250 fromNode.addPort(topologyElement.getFromPort());
251 isModified = true;
252 }
253 // Add the "to" port
254 Integer toPort = fromNode.getPort(topologyElement.getToPort());
255 if (toPort == null) {
256 toNode.addPort(topologyElement.getToPort());
257 isModified = true;
258 }
259 Node.Link link = fromNode.getLink(topologyElement.getFromPort());
260 if (link == null) {
261 fromNode.addLink(topologyElement.getFromPort(),
262 toNode,
263 topologyElement.getToPort());
264 isModified = true;
265 }
266
267 break;
268 }
Pavlin Radoslavov4839f6d2013-12-11 12:49:45 -0800269 case ELEMENT_UNKNOWN:
270 // TODO: Adding "assert(false);" here can be dangerous
271 break;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700272 }
273
274 return isModified;
275 }
276
277 /**
278 * Remove a topology element from the topology.
279 *
280 * @param topologyElement the topology element to remove.
281 * @return true if the topology was modified, otherwise false.
282 */
283 public boolean removeTopologyElement(TopologyElement topologyElement) {
284 boolean isModified = false;
285
286 switch (topologyElement.getType()) {
287 case ELEMENT_SWITCH: {
288 // Remove the switch
289 Node node = getNode(topologyElement.getSwitch());
290 if (node != null) {
291 removeNode(node);
292 isModified = true;
293 }
294 break;
295 }
296 case ELEMENT_PORT: {
297 // Find the switch
298 Node node = getNode(topologyElement.getSwitch());
299 if (node == null)
300 break;
301 // Remove the port for the switch
302 Integer port = node.getPort(topologyElement.getSwitchPort());
303 if (port != null) {
304 node.removePort(topologyElement.getSwitchPort());
305 isModified = true;
306 }
307 break;
308 }
309 case ELEMENT_LINK: {
310 // Find the "from" switch
311 Node fromNode = getNode(topologyElement.getFromSwitch());
312 if (fromNode == null)
313 break;
314 // Remove the link originating from the "from" port
315 Node.Link link = fromNode.getLink(topologyElement.getFromPort());
316 if (link != null) {
317 fromNode.removeLink(topologyElement.getFromPort());
318 isModified = true;
319 }
320 break;
321 }
Pavlin Radoslavov4839f6d2013-12-11 12:49:45 -0800322 case ELEMENT_UNKNOWN:
323 // TODO: Adding "assert(false);" here can be dangerous
324 break;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700325 }
326
327 return isModified;
328 }
329
330 /**
331 * Get a node for a given Node ID.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700332 *
333 * @param nodeId the Node ID to use.
334 * @return the corresponding Node if found, otherwise null.
335 */
336 Node getNode(long nodeId) {
337 return nodesMap.get(nodeId);
338 }
339
340 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700341 * Add a node for a given Node ID.
342 *
343 * @param nodeId the Node ID to use.
344 * @return the added Node.
345 */
346 Node addNode(long nodeId) {
347 Node node = new Node(nodeId);
348 nodesMap.put(nodeId, node);
349 return node;
350 }
351
352 /**
353 * Remove an existing node.
354 *
355 * @param node the Node to remove.
356 */
357 void removeNode(Node node) {
358 //
359 // Remove all ports one-by-one. This operation will also remove the
360 // incoming links originating from the neighbors.
361 //
Pavlin Radoslavovcfe7b402013-10-30 19:44:22 -0700362 // NOTE: We have to extract all Port IDs in advance, otherwise we
363 // cannot loop over the Ports collection and remove entries at the
364 // same time.
365 // TODO: If there is a large number of ports, the implementation
366 // below can be sub-optimal. It should be refactored as follows:
367 // 1. Modify removePort() to perform all the cleanup, except
368 // removing the Port entry from the portsMap
369 // 2. Call portsMap.clear() at the end of this method
370 // 3. In all other methods: if removePort() is called somewhere else,
371 // add an explicit removal of the Port entry from the portsMap.
372 //
373 List<Integer> allPortIdKeys = new LinkedList<Integer>();
374 allPortIdKeys.addAll(node.ports().keySet());
375 for (Integer portId : allPortIdKeys)
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700376 node.removePort(portId);
377
378 nodesMap.remove(node.nodeId);
379 }
380
381 /**
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700382 * Read topology state from the database.
383 *
384 * @param dbHandler the Graph Database handler to use.
385 */
yoshitomob292c622013-11-23 14:35:58 -0800386 public void readFromDatabase(DBOperation dbHandler) {
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700387 //
388 // Fetch the relevant info from the Switch and Port vertices
389 // from the Titan Graph.
390 //
391 Iterable<ISwitchObject> activeSwitches = dbHandler.getActiveSwitches();
392 for (ISwitchObject switchObj : activeSwitches) {
393 Vertex nodeVertex = switchObj.asVertex();
394 //
395 // The Switch info
396 //
397 String nodeDpid = nodeVertex.getProperty("dpid").toString();
398 long nodeId = HexString.toLong(nodeDpid);
399 Node me = nodesMap.get(nodeId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700400 if (me == null)
401 me = addNode(nodeId);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700402
403 //
404 // The local Port info
405 //
406 for (Vertex myPortVertex : nodeVertex.getVertices(Direction.OUT, "on")) {
407 // Ignore inactive ports
408 if (! myPortVertex.getProperty("state").toString().equals("ACTIVE"))
409 continue;
410
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700411 int myPort = 0;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700412 Object obj = myPortVertex.getProperty("number");
413 if (obj instanceof Short) {
414 myPort = (Short)obj;
415 } else if (obj instanceof Integer) {
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700416 myPort = (Integer)obj;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700417 }
418
419 //
420 // The neighbor Port info
421 //
422 for (Vertex neighborPortVertex : myPortVertex.getVertices(Direction.OUT, "link")) {
423 // Ignore inactive ports
424 if (! neighborPortVertex.getProperty("state").toString().equals("ACTIVE"))
425 continue;
426
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700427 int neighborPort = 0;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700428 obj = neighborPortVertex.getProperty("number");
429 if (obj instanceof Short) {
430 neighborPort = (Short)obj;
431 } else if (obj instanceof Integer) {
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700432 neighborPort = (Integer)obj;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700433 }
434 //
435 // The neighbor Switch info
436 //
437 for (Vertex neighborVertex : neighborPortVertex.getVertices(Direction.IN, "on")) {
438 // Ignore inactive switches
439 String state = neighborVertex.getProperty("state").toString();
440 if (! state.equals(SwitchState.ACTIVE.toString()))
441 continue;
442
443 String neighborDpid = neighborVertex.getProperty("dpid").toString();
444 long neighborId = HexString.toLong(neighborDpid);
445 Node neighbor = nodesMap.get(neighborId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700446 if (neighbor == null)
447 neighbor = addNode(neighborId);
448 me.addLink(myPort, neighbor, neighborPort);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700449 }
450 }
451 }
452 }
453 dbHandler.commit();
454 }
455}