Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
index 41b4957..d04e50a 100644
--- a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
+++ b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
@@ -17,7 +17,9 @@
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
 
+import net.onrc.onos.datagrid.web.DatagridWebRoutable;
 import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
 import net.onrc.onos.ofcontroller.topology.TopologyElement;
 import net.onrc.onos.ofcontroller.util.FlowEntry;
@@ -48,6 +50,7 @@
 
     protected final static Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
     protected IFloodlightProviderService floodlightProvider;
+    protected IRestApiService restApi;
 
     protected static final String HazelcastConfigFile = "datagridConfig";
     private HazelcastInstance hazelcastInstance = null;
@@ -163,6 +166,12 @@
 	 * @param event the notification event for the entry.
 	 */
 	public void entryAdded(EntryEvent event) {
+	    //
+	    // NOTE: Ignore Flow Entries Events originated by this instance
+	    //
+	    if (event.getMember().localMember())
+		return;
+
 	    Long keyLong = (Long)event.getKey();
 	    byte[] valueBytes = (byte[])event.getValue();
 
@@ -182,6 +191,12 @@
 	 * @param event the notification event for the entry.
 	 */
 	public void entryRemoved(EntryEvent event) {
+	    //
+	    // NOTE: Ignore Flow Entries Events originated by this instance
+	    //
+	    if (event.getMember().localMember())
+		return;
+
 	    Long keyLong = (Long)event.getKey();
 	    byte[] valueBytes = (byte[])event.getValue();
 
@@ -201,6 +216,12 @@
 	 * @param event the notification event for the entry.
 	 */
 	public void entryUpdated(EntryEvent event) {
+	    //
+	    // NOTE: Ignore Flow Entries Events originated by this instance
+	    //
+	    if (event.getMember().localMember())
+		return;
+
 	    Long keyLong = (Long)event.getKey();
 	    byte[] valueBytes = (byte[])event.getValue();
 
@@ -385,6 +406,7 @@
 	Collection<Class<? extends IFloodlightService>> l =
 	    new ArrayList<Class<? extends IFloodlightService>>();
 	l.add(IFloodlightProviderService.class);
+	l.add(IRestApiService.class);
         return l;
     }
 
@@ -397,6 +419,7 @@
     public void init(FloodlightModuleContext context)
 	throws FloodlightModuleException {
 	floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+	restApi = context.getServiceImpl(IRestApiService.class);
 
 	// Get the configuration file name and configure the Datagrid
 	Map<String, String> configMap = context.getConfigParams(this);
@@ -412,6 +435,8 @@
     @Override
     public void startUp(FloodlightModuleContext context) {
 	hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
+
+	restApi.addRestletRoutable(new DatagridWebRoutable());
     }
 
     /**
diff --git a/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
new file mode 100644
index 0000000..2c99ece
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.datagrid.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * REST API implementation for the Datagrid.
+ */
+public class DatagridWebRoutable implements RestletRoutable {
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/get/map/{map-name}/json", GetMapResource.class);
+        return router;
+    }
+
+    /**
+     * Set the base path for the Topology
+     */
+    @Override
+    public String basePath() {
+        return "/wm/datagrid";
+    }
+}
diff --git a/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java b/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java
new file mode 100644
index 0000000..124ac28
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java
@@ -0,0 +1,84 @@
+package net.onrc.onos.datagrid.web;
+
+import java.util.Collection;
+
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Datagrid REST API implementation: Get the state of a map.
+ *
+ * Valid map names:
+ *  - "all"        : Get all maps
+ *  - "flow"       : Get the Flows
+ *  - "flow-entry" : Get the Flow Entries
+ *  - "topology"   : Get the Topology
+ *
+ *   GET /wm/datagrid/get/map/{map-name}/json
+ */
+public class GetMapResource extends ServerResource {
+    protected final static Logger log = LoggerFactory.getLogger(GetMapResource.class);
+
+    /**
+     * Implement the API.
+     *
+     * @return a string with the state of the map(s).
+     */
+    @Get("json")
+    public String retrieve() {
+	String result = "";
+
+        IDatagridService datagridService =
+                (IDatagridService)getContext().getAttributes().
+                get(IDatagridService.class.getCanonicalName());
+
+        if (datagridService == null) {
+	    log.debug("ONOS Datagrid Service not found");
+            return result;
+	}
+
+	// Extract the arguments
+	String mapNameStr = (String)getRequestAttributes().get("map-name");
+
+	log.debug("Get Datagrid Map: " + mapNameStr);
+
+	//
+	// Get the Flows
+	//
+	if (mapNameStr.equals("flow") || mapNameStr.equals("all")) {
+	    Collection<FlowPath> flowPaths = datagridService.getAllFlows();
+	    result += "Flows:\n";
+	    for (FlowPath flowPath : flowPaths) {
+		result += flowPath.toString() + "\n";
+	    }
+	}
+
+	//
+	// Get the Flow Entries
+	//
+	if (mapNameStr.equals("flow-entry") || mapNameStr.equals("all")) {
+	    Collection<FlowEntry> flowEntries = datagridService.getAllFlowEntries();
+	    result += "Flow Entries:\n";
+	    for (FlowEntry flowEntry : flowEntries) {
+		result += flowEntry.toString() + "\n";
+	    }
+	}
+
+	if (mapNameStr.equals("topology") || mapNameStr.equals("all")) {
+	    Collection<TopologyElement> topologyElements = datagridService.getAllTopologyElements();
+	    result += "Topology:\n";
+	    for (TopologyElement topologyElement : topologyElements) {
+		result += topologyElement.toString() + "\n";
+	    }
+	}
+
+        return result;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
index 29deb94..0a6cd76 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
@@ -7,12 +7,10 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 
 import net.onrc.onos.datagrid.IDatagridService;
-import net.onrc.onos.ofcontroller.topology.ShortestPath;
 import net.onrc.onos.ofcontroller.topology.Topology;
 import net.onrc.onos.ofcontroller.topology.TopologyElement;
 import net.onrc.onos.ofcontroller.topology.TopologyManager;
@@ -33,7 +31,11 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Class for implementing the Path Computation and Path Maintenance.
+ * Class for FlowPath Maintenance.
+ * This class listens for FlowEvents to:
+ * - Maintain a local cache of the Network Topology.
+ * - Detect FlowPaths impacted by Topology change.
+ * - Recompute impacted FlowPath using cached Topology.
  */
 class FlowEventHandler extends Thread implements IFlowEventHandlerService {
     /** The logger. */
@@ -224,7 +226,7 @@
 		    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
 		}
 
-		allFlowPaths.remove(existingFlowPath.flowId());
+		allFlowPaths.remove(existingFlowPath.flowId().value());
 		modifiedFlowPaths.add(existingFlowPath);
 
 		break;
@@ -240,10 +242,10 @@
 	    TopologyElement topologyElement = eventEntry.eventData();
 	    switch (eventEntry.eventType()) {
 	    case ENTRY_ADD:
-		isTopologyModified = topology.addTopologyElement(topologyElement);
+		isTopologyModified |= topology.addTopologyElement(topologyElement);
 		break;
 	    case ENTRY_REMOVE:
-		isTopologyModified = topology.removeTopologyElement(topologyElement);
+		isTopologyModified |= topology.removeTopologyElement(topologyElement);
 		break;
 	    }
 	}
@@ -386,6 +388,8 @@
 	// Test whether the Flow Path needs to be recomputed
 	//
 	switch (flowPath.flowPathType()) {
+	case FP_TYPE_UNKNOWN:
+	    return false;		// Can't recompute on Unknown FlowType
 	case FP_TYPE_SHORTEST_PATH:
 	    break;
 	case FP_TYPE_EXPLICIT_PATH:
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
index dabe916..f187c27 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
@@ -24,7 +24,8 @@
 import com.tinkerpop.blueprints.Vertex;
 
 /**
- * A class for implementing the Shortest Path in a topology.
+ * Class to calculate a shortest DataPath between 2 SwitchPorts
+ * based on hops in Network Topology.
  */
 public class ShortestPath {
     /**
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
index 612b72a..d5ceb6a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
@@ -1,7 +1,9 @@
 package net.onrc.onos.ofcontroller.topology;
 
-import java.util.HashMap;
+import java.util.List;
+import java.util.LinkedList;
 import java.util.Map;
+import java.util.TreeMap;
 
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
@@ -44,9 +46,14 @@
     };
 
     public long nodeId;				// The node ID
-    public HashMap<Integer, Link> links;	// The links from this node
-    private HashMap<Integer, Link> reverseLinksMap; // The links to this node
-    private HashMap<Integer, Integer> portsMap;	// The ports for this node
+    public TreeMap<Integer, Link> links;	// The links from this node:
+						//     (src PortID -> Link)
+    private TreeMap<Integer, Link> reverseLinksMap; // The links to this node:
+						//     (dst PortID -> Link)
+    private TreeMap<Integer, Integer> portsMap;	// The ports on this node:
+						//     (PortID -> PortID)
+						// TODO: In the future will be:
+						//     (PortID -> Port)
 
     /**
      * Node constructor.
@@ -55,9 +62,9 @@
      */
     public Node(long nodeId) {
 	this.nodeId = nodeId;
-	links = new HashMap<Integer, Link>();
-	reverseLinksMap = new HashMap<Integer, Link>();
-	portsMap = new HashMap<Integer, Integer>();
+	links = new TreeMap<Integer, Link>();
+	reverseLinksMap = new TreeMap<Integer, Link>();
+	portsMap = new TreeMap<Integer, Integer>();
     }
 
     /**
@@ -78,7 +85,7 @@
      * @return the port if found, otherwise null.
      */
     public Integer getPort(int portId) {
-	return portsMap.get(nodeId);
+	return portsMap.get(portId);
     }
 
     /**
@@ -120,7 +127,7 @@
 
 	portsMap.remove(portId);
     }
-    
+
     /**
      * Get a link on a port to a neighbor.
      *
@@ -186,7 +193,7 @@
      * Default constructor.
      */
     public Topology() {
-	nodesMap = new HashMap<Long, Node>();
+	nodesMap = new TreeMap<Long, Node>();
     }
 
     /**
@@ -353,7 +360,20 @@
 	// Remove all ports one-by-one. This operation will also remove the
 	// incoming links originating from the neighbors.
 	//
-	for (Integer portId : node.ports().keySet())
+	// NOTE: We have to extract all Port IDs in advance, otherwise we
+	// cannot loop over the Ports collection and remove entries at the
+	// same time.
+	// TODO: If there is a large number of ports, the implementation
+	// below can be sub-optimal. It should be refactored as follows:
+	//   1. Modify removePort() to perform all the cleanup, except
+	//     removing the Port entry from the portsMap
+	//   2. Call portsMap.clear() at the end of this method
+	//   3. In all other methods: if removePort() is called somewhere else,
+	//      add an explicit removal of the Port entry from the portsMap.
+	//
+	List<Integer> allPortIdKeys = new LinkedList<Integer>();
+	allPortIdKeys.addAll(node.ports().keySet());
+	for (Integer portId : allPortIdKeys)
 	    node.removePort(portId);
 
 	nodesMap.remove(node.nodeId);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
index fe84654..574946d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
@@ -195,4 +195,15 @@
 	assert(false);
 	return null;
     }
+
+    /**
+     * Convert the Topology Element to a string.
+     *
+     * @return the Topology Element as a string.
+     */
+    @Override
+    public String toString() {
+	// For now, we just return the Element ID.
+	return elementId();
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
index ffe806a..c0e04f2 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
@@ -24,7 +24,10 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * A class for implementing Topology Network Service.
+ * A class for obtaining Topology Snapshot
+ * and PathComputation.
+ *
+ * TODO: PathComputation part should be refactored out to separate class.
  */
 public class TopologyManager implements IFloodlightModule,
 					ITopologyNetService {
diff --git a/web/get_datagrid.py b/web/get_datagrid.py
new file mode 100755
index 0000000..2d26846
--- /dev/null
+++ b/web/get_datagrid.py
@@ -0,0 +1,84 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+from flask import Flask, json, Response, render_template, make_response, request
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+pp = pprint.PrettyPrinter(indent=4)
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+  print '%s' % (txt)
+
+def debug(txt):
+  if DEBUG:
+    print '%s' % (txt)
+
+# @app.route("/wm/datagrid/get/map/<map-name>/json ")
+# Sample output:
+
+def print_datagrid_map(parsedResult):
+  print '%s' % (parsedResult)
+
+def get_datagrid_map(map_name):
+  try:
+    command = "curl -s \"http://%s:%s/wm/datagrid/get/map/%s/json\"" % (ControllerIP, ControllerPort, map_name)
+    debug("get_datagrid_map %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+      print "No Map found"
+      return;
+
+    # TODO: For now, the string is not JSON-formatted
+    # parsedResult = json.loads(result)
+    parsedResult = result
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  print_datagrid_map(parsedResult)
+
+
+if __name__ == "__main__":
+  usage_msg1 = "Usage:\n"
+  usage_msg2 = "%s <map_name> : Print datagrid map with name of <map_name>\n" % (sys.argv[0])
+  usage_msg3 = "    Valid map names:\n"
+  usage_msg4 = "        all              : Print all maps\n"
+  usage_msg5 = "        flow             : Print all flows\n"
+  usage_msg6 = "        flow-entry       : Print all flow entries\n"
+  usage_msg7 = "        topology         : Print the topology\n"
+  usage_msg = usage_msg1 + usage_msg2 + usage_msg3 + usage_msg4 + usage_msg5
+  usage_msg = usage_msg + usage_msg6 + usage_msg7
+
+  # app.debug = False;
+
+  # Usage info
+  if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+    print(usage_msg)
+    exit(0)
+
+  # Check arguments
+  if len(sys.argv) < 2:
+    log_error(usage_msg)
+    exit(1)
+
+  # Do the work
+  get_datagrid_map(sys.argv[1])