blob: dedb58972b65d40ad9276d242c5fd3998c43d2ed [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;
16
17/**
18 * A class for storing Node and Link information for fast computation
19 * of shortest paths.
20 */
21class Node {
22 /**
23 * A class for storing Link information for fast computation of shortest
24 * paths.
25 */
26 class Link {
27 public Node me; // The node this link originates from
28 public Node neighbor; // The neighbor node on the other side
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070029 public int myPort; // Local port ID for the link
30 public int neighborPort; // Neighbor port ID for the link
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070031
32 /**
33 * Link constructor.
34 *
35 * @param me the node this link originates from.
36 * @param the neighbor node on the other side of the link.
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070037 * @param myPort local port ID for the link.
38 * @param neighborPort neighbor port ID for the link.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070039 */
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070040 public Link(Node me, Node neighbor, int myPort, int neighborPort) {
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070041 this.me = me;
42 this.neighbor = neighbor;
43 this.myPort = myPort;
44 this.neighborPort = neighborPort;
45 }
46 };
47
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070048 public long nodeId; // The node ID
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070049 public TreeMap<Integer, Link> links; // The links from this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070050 // (src PortID -> Link)
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070051 private TreeMap<Integer, Link> reverseLinksMap; // The links to this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070052 // (dst PortID -> Link)
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070053 private TreeMap<Integer, Integer> portsMap; // The ports on this node:
Pavlin Radoslavovf9161ca2013-10-31 17:08:12 -070054 // (PortID -> PortID)
55 // TODO: In the future will be:
56 // (PortID -> Port)
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070057
58 /**
59 * Node constructor.
60 *
61 * @param nodeId the node ID.
62 */
63 public Node(long nodeId) {
64 this.nodeId = nodeId;
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -070065 links = new TreeMap<Integer, Link>();
66 reverseLinksMap = new TreeMap<Integer, Link>();
67 portsMap = new TreeMap<Integer, Integer>();
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070068 }
69
70 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070071 * Get all ports.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070072 *
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070073 * @return all ports.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -070074 */
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070075 public Map<Integer, Integer> ports() {
76 return portsMap;
77 }
78
79 /**
80 * Get the port for a given Port ID.
81 *
82 * Note: For now the port itself is just the Port ID. In the future
83 * it might contain more information.
84 *
85 * @return the port if found, otherwise null.
86 */
87 public Integer getPort(int portId) {
Yuta HIGUCHIdbc8c442013-10-31 10:15:37 -070088 return portsMap.get(portId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070089 }
90
91 /**
92 * Add a port for a given Port ID.
93 *
94 * Note: For now the port itself is just the Port ID. In the future
95 * it might contain more information.
96 *
97 * @param portId the Port ID of the port to add.
98 * @return the added Port.
99 */
100 Integer addPort(int portId) {
101 Integer port = new Integer(portId);
102 portsMap.put(portId, port);
103 return port;
104 }
105
106 /**
107 * Remove a port for a given Port ID.
108 *
109 * NOTE: The outgoing and incoming links using this port are removed as
110 * well.
111 */
112 void removePort(int portId) {
113 // Remove the outgoing link
114 Link link = getLink(portId);
115 if (link != null) {
116 link.neighbor.removeReverseLink(link);
117 removeLink(portId);
118 }
119
120 // Remove the incoming link
121 Link reverseLink = reverseLinksMap.get(portId);
122 if (reverseLink != null) {
123 // NOTE: reverseLink.myPort is the neighbor's outgoing port
Pavlin Radoslavov823f3222013-11-04 21:57:31 -0800124 reverseLink.me.removeLink(reverseLink.myPort);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700125 removeReverseLink(reverseLink);
126 }
127
128 portsMap.remove(portId);
129 }
Yuta HIGUCHI63ee5a82013-10-31 10:16:15 -0700130
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700131 /**
132 * Get a link on a port to a neighbor.
133 *
134 * @param myPortId the local port ID for the link to the neighbor.
135 * @return the link if found, otherwise null.
136 */
137 public Link getLink(int myPortId) {
138 return links.get(myPortId);
139 }
140
141 /**
142 * Add a link to a neighbor.
143 *
144 * @param myPortId the local port ID for the link to the neighbor.
145 * @param neighbor the neighbor for the link.
146 * @param neighborPortId the neighbor port ID for the link.
147 * @return the added Link.
148 */
149 public Link addLink(int myPortId, Node neighbor, int neighborPortId) {
150 Link link = new Link(this, neighbor, myPortId, neighborPortId);
151 links.put(myPortId, link);
152 neighbor.addReverseLink(link);
153 return link;
154 }
155
156 /**
157 * Add a reverse link from a neighbor.
158 *
159 * @param link the reverse link from a neighbor to add.
160 */
161 private void addReverseLink(Link link) {
162 // NOTE: link.neghborPort is my port
163 reverseLinksMap.put(link.neighborPort, link);
164 }
165
166 /**
167 * Remove a link to a neighbor.
168 *
169 * @param myPortId the local port ID for the link to the neighbor.
170 */
171 public void removeLink(int myPortId) {
172 links.remove(myPortId);
173 }
174
175 /**
176 * Remove a reverse link from a neighbor.
177 *
178 * @param link the reverse link from a neighbor to remove.
179 */
180 private void removeReverseLink(Link link) {
181 // NOTE: link.neghborPort is my port
182 reverseLinksMap.remove(link.neighborPort);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700183 }
184};
185
186/**
187 * A class for storing topology information.
188 */
189public class Topology {
190 private Map<Long, Node> nodesMap; // The dpid->Node mapping
191
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700192 /**
193 * Default constructor.
194 */
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700195 public Topology() {
Pavlin Radoslavovdf656d62013-10-31 17:24:32 -0700196 nodesMap = new TreeMap<Long, Node>();
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700197 }
198
199 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700200 * Add a topology element to the topology.
201 *
202 * @param topologyElement the topology element to add.
203 * @return true if the topology was modified, otherwise false.
204 */
205 public boolean addTopologyElement(TopologyElement topologyElement) {
206 boolean isModified = false;
207
208 switch (topologyElement.getType()) {
209 case ELEMENT_SWITCH: {
210 // Add the switch
211 Node node = getNode(topologyElement.getSwitch());
212 if (node == null) {
213 node = addNode(topologyElement.getSwitch());
214 isModified = true;
215 }
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700216 break;
217 }
218 case ELEMENT_PORT: {
219 // Add the switch
220 Node node = getNode(topologyElement.getSwitch());
221 if (node == null) {
222 node = addNode(topologyElement.getSwitch());
223 isModified = true;
224 }
225 // Add the port for the switch
226 Integer port = node.getPort(topologyElement.getSwitchPort());
227 if (port == null) {
228 node.addPort(topologyElement.getSwitchPort());
229 isModified = true;
230 }
231 break;
232 }
233 case ELEMENT_LINK: {
234 // Add the "from" switch
235 Node fromNode = getNode(topologyElement.getFromSwitch());
236 if (fromNode == null) {
237 fromNode = addNode(topologyElement.getFromSwitch());
238 isModified = true;
239 }
240 // Add the "to" switch
241 Node toNode = getNode(topologyElement.getToSwitch());
242 if (toNode == null) {
243 toNode = addNode(topologyElement.getToSwitch());
244 isModified = true;
245 }
246 // Add the "from" port
247 Integer fromPort = fromNode.getPort(topologyElement.getFromPort());
248 if (fromPort == null) {
249 fromNode.addPort(topologyElement.getFromPort());
250 isModified = true;
251 }
252 // Add the "to" port
253 Integer toPort = fromNode.getPort(topologyElement.getToPort());
254 if (toPort == null) {
255 toNode.addPort(topologyElement.getToPort());
256 isModified = true;
257 }
258 Node.Link link = fromNode.getLink(topologyElement.getFromPort());
259 if (link == null) {
260 fromNode.addLink(topologyElement.getFromPort(),
261 toNode,
262 topologyElement.getToPort());
263 isModified = true;
264 }
265
266 break;
267 }
Pavlin Radoslavov4839f6d2013-12-11 12:49:45 -0800268 case ELEMENT_UNKNOWN:
269 // TODO: Adding "assert(false);" here can be dangerous
270 break;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700271 }
272
273 return isModified;
274 }
275
276 /**
277 * Remove a topology element from the topology.
278 *
279 * @param topologyElement the topology element to remove.
280 * @return true if the topology was modified, otherwise false.
281 */
282 public boolean removeTopologyElement(TopologyElement topologyElement) {
283 boolean isModified = false;
284
285 switch (topologyElement.getType()) {
286 case ELEMENT_SWITCH: {
287 // Remove the switch
288 Node node = getNode(topologyElement.getSwitch());
289 if (node != null) {
290 removeNode(node);
291 isModified = true;
292 }
293 break;
294 }
295 case ELEMENT_PORT: {
296 // Find the switch
297 Node node = getNode(topologyElement.getSwitch());
298 if (node == null)
299 break;
300 // Remove the port for the switch
301 Integer port = node.getPort(topologyElement.getSwitchPort());
302 if (port != null) {
303 node.removePort(topologyElement.getSwitchPort());
304 isModified = true;
305 }
306 break;
307 }
308 case ELEMENT_LINK: {
309 // Find the "from" switch
310 Node fromNode = getNode(topologyElement.getFromSwitch());
311 if (fromNode == null)
312 break;
313 // Remove the link originating from the "from" port
314 Node.Link link = fromNode.getLink(topologyElement.getFromPort());
315 if (link != null) {
316 fromNode.removeLink(topologyElement.getFromPort());
317 isModified = true;
318 }
319 break;
320 }
Pavlin Radoslavov4839f6d2013-12-11 12:49:45 -0800321 case ELEMENT_UNKNOWN:
322 // TODO: Adding "assert(false);" here can be dangerous
323 break;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700324 }
325
326 return isModified;
327 }
328
329 /**
330 * Get a node for a given Node ID.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700331 *
332 * @param nodeId the Node ID to use.
333 * @return the corresponding Node if found, otherwise null.
334 */
335 Node getNode(long nodeId) {
336 return nodesMap.get(nodeId);
337 }
338
339 /**
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700340 * Add a node for a given Node ID.
341 *
342 * @param nodeId the Node ID to use.
343 * @return the added Node.
344 */
345 Node addNode(long nodeId) {
346 Node node = new Node(nodeId);
347 nodesMap.put(nodeId, node);
348 return node;
349 }
350
351 /**
352 * Remove an existing node.
353 *
354 * @param node the Node to remove.
355 */
356 void removeNode(Node node) {
357 //
358 // Remove all ports one-by-one. This operation will also remove the
359 // incoming links originating from the neighbors.
360 //
Pavlin Radoslavovcfe7b402013-10-30 19:44:22 -0700361 // NOTE: We have to extract all Port IDs in advance, otherwise we
362 // cannot loop over the Ports collection and remove entries at the
363 // same time.
364 // TODO: If there is a large number of ports, the implementation
365 // below can be sub-optimal. It should be refactored as follows:
366 // 1. Modify removePort() to perform all the cleanup, except
367 // removing the Port entry from the portsMap
368 // 2. Call portsMap.clear() at the end of this method
369 // 3. In all other methods: if removePort() is called somewhere else,
370 // add an explicit removal of the Port entry from the portsMap.
371 //
372 List<Integer> allPortIdKeys = new LinkedList<Integer>();
373 allPortIdKeys.addAll(node.ports().keySet());
374 for (Integer portId : allPortIdKeys)
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700375 node.removePort(portId);
376
377 nodesMap.remove(node.nodeId);
378 }
379
380 /**
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700381 * Read topology state from the database.
382 *
383 * @param dbHandler the Graph Database handler to use.
Naoki Shiota0abe38d2014-01-07 15:31:22 -0800384 * @return true if topology is updated. In other words,
385 * topology read from database is different from current topology.
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700386 */
Naoki Shiota0abe38d2014-01-07 15:31:22 -0800387 public boolean readFromDatabase(GraphDBOperation dbHandler) {
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700388 //
389 // Fetch the relevant info from the Switch and Port vertices
390 // from the Titan Graph.
391 //
Naoki Shiota0abe38d2014-01-07 15:31:22 -0800392
393 Map<Long,Node> oldNodesMap = nodesMap;
394 nodesMap = new TreeMap<Long,Node>();
395
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700396 Iterable<ISwitchObject> activeSwitches = dbHandler.getActiveSwitches();
397 for (ISwitchObject switchObj : activeSwitches) {
398 Vertex nodeVertex = switchObj.asVertex();
399 //
400 // The Switch info
401 //
402 String nodeDpid = nodeVertex.getProperty("dpid").toString();
403 long nodeId = HexString.toLong(nodeDpid);
404 Node me = nodesMap.get(nodeId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700405 if (me == null)
406 me = addNode(nodeId);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700407
408 //
409 // The local Port info
410 //
411 for (Vertex myPortVertex : nodeVertex.getVertices(Direction.OUT, "on")) {
412 // Ignore inactive ports
413 if (! myPortVertex.getProperty("state").toString().equals("ACTIVE"))
414 continue;
415
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700416 int myPort = 0;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700417 Object obj = myPortVertex.getProperty("number");
418 if (obj instanceof Short) {
419 myPort = (Short)obj;
420 } else if (obj instanceof Integer) {
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700421 myPort = (Integer)obj;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700422 }
423
424 //
425 // The neighbor Port info
426 //
427 for (Vertex neighborPortVertex : myPortVertex.getVertices(Direction.OUT, "link")) {
428 // Ignore inactive ports
429 if (! neighborPortVertex.getProperty("state").toString().equals("ACTIVE"))
430 continue;
431
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700432 int neighborPort = 0;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700433 obj = neighborPortVertex.getProperty("number");
434 if (obj instanceof Short) {
435 neighborPort = (Short)obj;
436 } else if (obj instanceof Integer) {
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700437 neighborPort = (Integer)obj;
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700438 }
439 //
440 // The neighbor Switch info
441 //
442 for (Vertex neighborVertex : neighborPortVertex.getVertices(Direction.IN, "on")) {
443 // Ignore inactive switches
444 String state = neighborVertex.getProperty("state").toString();
445 if (! state.equals(SwitchState.ACTIVE.toString()))
446 continue;
447
448 String neighborDpid = neighborVertex.getProperty("dpid").toString();
449 long neighborId = HexString.toLong(neighborDpid);
450 Node neighbor = nodesMap.get(neighborId);
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700451 if (neighbor == null)
452 neighbor = addNode(neighborId);
453 me.addLink(myPort, neighbor, neighborPort);
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700454 }
455 }
456 }
457 }
458 dbHandler.commit();
Naoki Shiota0abe38d2014-01-07 15:31:22 -0800459 return ! compareTopology(oldNodesMap, nodesMap);
460 }
461
462 // TODO Merge into loops in readFromDatabase() can reduce execution time.
463 /**
464 * Check given two topology are identical or not.
465 * @param topo1
466 * @param topo2
467 * @return true if identical
468 */
469 private boolean compareTopology(Map<Long,Node> topo1, Map<Long,Node> topo2) {
470 if (topo1.size() != topo2.size()) {
471 return false;
472 }
473
474 for (Map.Entry<Long,Node> nodeEntry : topo1.entrySet()) {
475 Long dpid = nodeEntry.getKey();
476 if (! topo2.containsKey(dpid)) {
477 return false;
478 }
479
480 Node n1 = nodeEntry.getValue();
481 Node n2 = topo2.get(dpid);
482
483 // check port identity
484 if (n1.ports().size() != n2.ports().size()) {
485 return false;
486 }
487 for (Integer port : n1.ports().keySet()) {
488 if (! n2.ports().containsKey(port)) {
489 return false;
490 }
491 }
492
493 // check link identity
494 if (n1.links.size() != n2.links.size()) {
495 return false;
496 }
497 for (Map.Entry<Integer, Node.Link> linkEntry : n1.links.entrySet()) {
498 Integer p1 = linkEntry.getKey();
499 Node.Link l1 = linkEntry.getValue();
500
501 if (! n2.links.containsKey(p1)) {
502 return false;
503 }
504 Node.Link l2 = n2.links.get(p1);
505
506 // Supposition: Link's "me" and "neighbor" is properly set.
507 if (l1.myPort != l2.myPort ||
508 l1.neighborPort != l2.neighborPort) {
509 return false;
510 }
511 }
512 }
513 return true;
514 }
515
516 // Only for debug use
517 @Override
518 public String toString() {
519 long numNodes = nodesMap.size();
520 long numLinks = 0;
521 for (Map.Entry<Long, Node> entry : nodesMap.entrySet()) {
522 Node n = entry.getValue();
523 for (Map.Entry<Integer, Node.Link> linkEntry : n.links.entrySet()) {
524 if (n.nodeId > linkEntry.getValue().neighbor.nodeId) {
525 ++numLinks;
526 }
527 }
528 }
529 return "Topology has " + numNodes + " Nodes and " + numLinks + " Links.";
Pavlin Radoslavov15954d42013-10-19 15:29:04 -0700530 }
531}