Merge pull request #446 from jonohart/master

Addressing link discovery bugs
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/core/ILinkStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
index 483fbda..c8312d4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
@@ -43,7 +43,17 @@
 	 *  If only dpid is set all links associated with Switch are retrieved
 	 */
 	public List<Link> getLinks(Long dpid, short port);
+
 	public List<Link> getLinks(String dpid);
+
+	/**
+	 * Get list of all reverse links connected to the switch specified by
+	 * given DPID.
+	 * @param dpid DPID of desired switch.
+	 * @return List of reverse links. Empty list if no port was found.
+	 */
+	public List<Link> getReverseLinks(String dpid);
+
 	public List<Link> getActiveLinks();
 
 	public LinkInfo getLinkInfo(Link link);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
index 6f13080..ed6807b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -129,6 +129,10 @@
 		@JsonIgnore
 		@Adjacency(label="link")
 		public Iterable<IPortObject> getLinkedPorts();
+
+		@JsonIgnore
+		@Adjacency(label="link",direction = Direction.IN)
+		public Iterable<IPortObject> getReverseLinkedPorts();
 		
 		@Adjacency(label="link")
 		public void removeLink(final IPortObject dest_port);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
index 68e2c6f..09e87ca 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
@@ -306,6 +306,38 @@
 	}
 
 	/**
+	 * Get list of all reverse links connected to the switch specified by
+	 * given DPID.
+	 * @param dpid DPID of desired switch.
+	 * @return List of reverse links. Empty list if no port was found.
+	 */
+	@Override
+	public List<Link> getReverseLinks(String dpid) {
+		List<Link> links = new ArrayList<Link>();
+
+		ISwitchObject srcSw = op.searchSwitch(dpid);
+		
+		if(srcSw != null) {
+			for(IPortObject srcPort : srcSw.getPorts()) {
+				for(IPortObject dstPort : srcPort.getReverseLinkedPorts()) {
+					ISwitchObject dstSw = dstPort.getSwitch();
+					if(dstSw != null) {
+		        		Link link = new Link(
+							HexString.toLong(dstSw.getDPID()),
+							dstPort.getNumber(),
+					
+							HexString.toLong(srcSw.getDPID()),
+							srcPort.getNumber());
+		        		links.add(link);
+					}
+				}
+			}
+		}
+		
+		return links;
+	}
+
+	/**
 	 * Get list of all links whose state is ACTIVE.
 	 * @return List of active links. Empty list if no port was found.
 	 */
diff --git a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
index 14cffd8..563d575 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -3,6 +3,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -217,6 +218,19 @@
 				topologyElement.addSwitchPort(port.getPortNumber());
 			    }
 			    datagridService.notificationSendTopologyElementAdded(topologyElement);
+			    // Add all links that might be connected already
+			    List<Link> links = linkStore.getLinks(HexString.toHexString(sw.getId()));
+			    // Add all reverse links as well
+			    List<Link> reverseLinks = linkStore.getReverseLinks(HexString.toHexString(sw.getId()));
+			    links.addAll(reverseLinks);
+			    for (Link link : links) {
+				TopologyElement topologyElementLink =
+				    new TopologyElement(link.getSrc(),
+							link.getSrcPort(),
+							link.getDst(),
+							link.getDstPort());
+				datagridService.notificationSendTopologyElementAdded(topologyElementLink);
+			    }
 			}
 		}
 	}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
index d06c62c..926788f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
@@ -326,6 +326,44 @@
     }
 
     /**
+     * Delete a flow entry from the Network MAP.
+     *
+     * @param dbHandler the Graph Database handler to use.
+     * @param flowObj the corresponding Flow Path object for the Flow Entry.
+     * @param flowEntry the Flow Entry to delete.
+     * @return true on success, otherwise false.
+     */
+    static boolean deleteFlowEntry(GraphDBOperation dbHandler,
+				   IFlowPath flowObj,
+				   FlowEntry flowEntry) {
+	IFlowEntry flowEntryObj = null;
+	try {
+	    flowEntryObj = dbHandler.searchFlowEntry(flowEntry.flowEntryId());
+	} catch (Exception e) {
+	    log.error(":deleteFlowEntry FlowEntryId:{} failed",
+		      flowEntry.flowEntryId().toString());
+	    return false;
+	}
+	//
+	// TODO: Don't print an error for now, because multiple controller
+	// instances might be deleting the same flow entry.
+	//
+	/*
+	if (flowEntryObj == null) {
+	    log.error(":deleteFlowEntry FlowEntryId:{} failed: FlowEntry object not found",
+		      flowEntry.flowEntryId().toString());
+	    return false;
+	}
+	*/
+	if (flowEntryObj == null)
+	    return true;
+
+	flowObj.removeFlowEntry(flowEntryObj);
+	dbHandler.removeFlowEntry(flowEntryObj);
+	return true;
+    }
+
+    /**
      * Delete all previously added flows.
      *
      * @param dbHandler the Graph Database handler to use.
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/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index 4465835..1b4dc4d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -551,6 +551,18 @@
     }
 
     /**
+     * Delete a flow entry from the Network MAP.
+     *
+     * @param flowObj the corresponding Flow Path object for the Flow Entry.
+     * @param flowEntry the Flow Entry to delete.
+     * @return true on success, otherwise false.
+     */
+    private boolean deleteFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
+	return FlowDatabaseOperation.deleteFlowEntry(dbHandler, flowObj,
+						     flowEntry);
+    }
+
+    /**
      * Delete all previously added flows.
      *
      * @return true on success, otherwise false.
@@ -840,7 +852,6 @@
      * Flow Entries.
      */
     public void pushModifiedFlowEntries(Collection<FlowPath> modifiedFlowPaths) {
-
 	// TODO: For now, the pushing of Flow Entries is disabled
 	if (true)
 	    return;
@@ -848,57 +859,83 @@
 	Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
 
 	for (FlowPath flowPath : modifiedFlowPaths) {
+	    //
+	    // Find the Flow Path in the Network MAP.
+	    // NOTE: The Flow Path might not be found if the Flow was just
+	    // removed by some other controller instance.
+	    //
 	    IFlowPath flowObj = dbHandler.searchFlowPath(flowPath.flowId());
-	    if (flowObj == null) {
-		String logMsg = "Cannot find Network MAP entry for Flow Path " +
-		    flowPath.flowId();
-		log.error(logMsg);
-		continue;
-	    }
 
+	    boolean isFlowEntryDeleted = false;
 	    for (FlowEntry flowEntry : flowPath.flowEntries()) {
+		System.out.println("PAVPAV: P40: Updating Flow Entry: " + flowEntry.toString());
+
 		if (flowEntry.flowEntrySwitchState() !=
 		    FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED) {
 		    continue;		// No need to update the entry
 		}
+		if (flowEntry.flowEntryUserState() ==
+		    FlowEntryUserState.FE_USER_DELETE) {
+		    isFlowEntryDeleted = true;
+		}
 
+		//
+		// Install the Flow Entries into my switches
+		//
 		IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
-		if (mySwitch == null)
-		    continue;		// Ignore the entry: not my switch
+		if (mySwitch != null) {
+		    //
+		    // Assign the FlowEntry ID if needed
+		    //
+		    if (! flowEntry.isValidFlowEntryId()) {
+			long id = getNextFlowEntryId();
+			flowEntry.setFlowEntryId(new FlowEntryId(id));
+		    }
 
-		//
-		// Assign the FlowEntry ID if needed
-		//
-		if (! flowEntry.isValidFlowEntryId()) {
-		    long id = getNextFlowEntryId();
-		    flowEntry.setFlowEntryId(new FlowEntryId(id));
+		    //
+		    // Install the Flow Entry into the switch
+		    //
+		    if (! installFlowEntry(mySwitch, flowPath, flowEntry)) {
+			String logMsg = "Cannot install Flow Entry " +
+			    flowEntry.flowEntryId() +
+			    " from Flow Path " + flowPath.flowId() +
+			    " on switch " + flowEntry.dpid();
+			log.error(logMsg);
+			continue;
+		    }
+
+		    //
+		    // NOTE: Here we assume that the switch has been
+		    // successfully updated.
+		    //
+		    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
 		}
 
 		//
-		// Install the Flow Entry into the switch
+		// TODO: For now Flow Entries are removed from the Datagrid
+		// and from the Network Map by all instances, even if this
+		// Flow Entry is not for our switches.
 		//
-		if (! installFlowEntry(mySwitch, flowPath, flowEntry)) {
-		    String logMsg = "Cannot install Flow Entry " +
-			flowEntry.flowEntryId() +
-			" from Flow Path " + flowPath.flowId() +
-			" on switch " + flowEntry.dpid();
-		    log.error(logMsg);
-		    continue;
-		}
+		// This is needed to handle the case a switch going down:
+		// it has no Master controller instance, hence no
+		// controller instance will cleanup its flow entries.
+		// This is sub-optimal: we need to elect a controller
+		// instance to handle the cleanup of such orphaned flow
+		// entries.
+		//
 
 		//
-		// NOTE: Here we assume that the switch has been successfully
-		// updated.
-		//
-		flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
-		//
 		// Write the Flow Entry to the Datagrid
 		//
 		switch (flowEntry.flowEntryUserState()) {
 		case FE_USER_ADD:
+		    if (mySwitch == null)
+			break;	// Install only flow entries for my switches
 		    datagridService.notificationSendFlowEntryAdded(flowEntry);
 		    break;
 		case FE_USER_MODIFY:
+		    if (mySwitch == null)
+			break;	// Install only flow entries for my switches
 		    datagridService.notificationSendFlowEntryUpdated(flowEntry);
 		    break;
 		case FE_USER_DELETE:
@@ -909,15 +946,41 @@
 		//
 		// Write the Flow Entry to the Network Map
 		//
-		try {
-		    if (addFlowEntry(flowObj, flowEntry) == null) {
-			String logMsg = "Cannot write to Network MAP Flow Entry " +
-			    flowEntry.flowEntryId() +
-			    " from Flow Path " + flowPath.flowId() +
-			    " on switch " + flowEntry.dpid();
-			log.error(logMsg);
+		if (mySwitch == null) {
+		    if (flowEntry.flowEntryUserState() !=
+			FlowEntryUserState.FE_USER_DELETE) {
 			continue;
 		    }
+		    if (! flowEntry.isValidFlowEntryId())
+			continue;
+		}
+		if (flowObj == null) {
+		    String logMsg = "Cannot find Network MAP entry for Flow Path " + flowPath.flowId();
+		    continue;
+		}
+		try {
+		    switch (flowEntry.flowEntryUserState()) {
+		    case FE_USER_ADD:
+			// FALLTHROUGH
+		    case FE_USER_MODIFY:
+			if (addFlowEntry(flowObj, flowEntry) == null) {
+			    String logMsg = "Cannot write to Network MAP Flow Entry " +
+				flowEntry.flowEntryId() +
+				" from Flow Path " + flowPath.flowId() +
+				" on switch " + flowEntry.dpid();
+			    log.error(logMsg);
+			}
+			break;
+		    case FE_USER_DELETE:
+			if (deleteFlowEntry(flowObj, flowEntry) == false) {
+			    String logMsg = "Cannot remove from Network MAP Flow Entry " +
+				flowEntry.flowEntryId() +
+				" from Flow Path " + flowPath.flowId() +
+				" on switch " + flowEntry.dpid();
+			    log.error(logMsg);
+			}
+			break;
+		    }
 		} catch (Exception e) {
 		    String logMsg = "Exception writing Flow Entry to Network MAP";
 		    log.debug(logMsg);
@@ -925,6 +988,26 @@
 		    continue;
 		}
 	    }
+
+	    //
+	    // Remove Flow Entries that were deleted
+	    //
+	    // NOTE: We create a new ArrayList, and add only the Flow Entries
+	    // that are NOT FE_USER_DELETE.
+	    // This is sub-optimal: if it adds notable processing cost,
+	    // the Flow Entries container should be changed to LinkedList
+	    // or some other container that has O(1) cost of removing an entry.
+	    //
+	    if (isFlowEntryDeleted) {
+		ArrayList<FlowEntry> newFlowEntries = new ArrayList<FlowEntry>();
+		for (FlowEntry flowEntry : flowPath.flowEntries()) {
+		    if (flowEntry.flowEntryUserState() !=
+			FlowEntryUserState.FE_USER_DELETE) {
+			newFlowEntries.add(flowEntry);
+		    }
+		}
+		flowPath.dataPath().setFlowEntries(newFlowEntries);
+	    }
 	}
 
 	dbHandler.commit();
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/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
index abb8809..4aea22a 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
@@ -256,6 +256,21 @@
 		Link linkToVerifyNot = createFeasibleLink();
 		assertFalse(links.contains(linkToVerifyNot));
 	}
+
+	/**
+	 * Test if {@link LinkStorageImpl#getReverseLinks(String)} can correctly return Links connected to specific MAC address.
+	 */
+	@Test
+	public void testGetReverseLinks_ByString() {
+		Link linkToVeryfy = createExistingLink();
+		String dpid = HexString.toHexString(linkToVeryfy.getDst());
+		
+		List<Link> links = linkStorage.getReverseLinks(dpid);
+		assertTrue(links.contains(linkToVeryfy));
+
+		Link linkToVerifyNot = createFeasibleLink();
+		assertFalse(links.contains(linkToVerifyNot));
+	}
 	
 	/**
 	 * Test if {@link LinkStorageImpl#deleteLink(Link)} can correctly delete a Link.
@@ -447,6 +462,35 @@
 	}
 
 	/**
+	 * Class defines a function called back when {@link IPortObject#getReverseLinkedPorts()} is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class GetReverseLinkedPortsCallback implements IAnswer< Iterable<IPortObject> > {
+		private long dpid;
+		private short port;
+		
+		public GetReverseLinkedPortsCallback(long dpid, short port) {
+			this.dpid = dpid;
+			this.port = port;
+		}
+
+		@Override
+		public Iterable<IPortObject> answer() throws Throwable {
+			List<IPortObject> ports = new ArrayList<IPortObject>();
+
+			for(Link lk : links) {
+				if(lk.getDst() == dpid && lk.getDstPort() == port) {
+					ports.add(createMockPort(lk.getSrc(), lk.getSrcPort()));
+				}
+			}
+
+			return ports;
+		}
+		
+	}
+
+	/**
 	 * Class defines a function called back when {@link LinkStorageImplTest} is called.
 	 * @author Naoki Shiota
 	 *
@@ -567,6 +611,9 @@
 		
 		// Mock getLinkPorts() method
 		EasyMock.expect(mockPort.getLinkedPorts()).andAnswer(new GetLinkedPortsCallback(dpid, number)).anyTimes();
+
+		// Mock getReverseLinkPorts() method
+		EasyMock.expect(mockPort.getReverseLinkedPorts()).andAnswer(new GetReverseLinkedPortsCallback(dpid, number)).anyTimes();
 		
 		// Mock getSwitch() method
 		EasyMock.expect(mockPort.getSwitch()).andAnswer(new GetSwitchCallback(dpid)).anyTimes();
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
index dfe6ccf..e4b200f 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
@@ -162,6 +162,7 @@
 		private ISwitchObject sw;
 		
 		private List<IPortObject> linkedPorts;
+		private List<IPortObject> reverseLinkedPorts;
 		private List<IDeviceObject> devices;
 		private List<IFlowEntry> inflows,outflows;
 		
@@ -179,6 +180,7 @@
 			type = "port";
 
 			linkedPorts = new ArrayList<IPortObject>();
+			reverseLinkedPorts = new ArrayList<IPortObject>();
 			linkedPortsToAdd = new ArrayList<IPortObject>();
 			linkedPortsToRemove = new ArrayList<IPortObject>();
 			devices = new ArrayList<IDeviceObject>();
@@ -289,6 +291,9 @@
 		public Iterable<IPortObject> getLinkedPorts() { return linkedPorts; }
 
 		@Override
+		public Iterable<IPortObject> getReverseLinkedPorts() { return reverseLinkedPorts; }
+
+		@Override
 		public void removeLink(IPortObject dest_port) { linkedPortsToRemove.add(dest_port); }
 
 		@Override
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])