Merge "adding some logging and checking Intent type more carefully" into dev/ramcloud-new-datamodel
diff --git a/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
index 28399c8..a8c3f5b 100755
--- a/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
+++ b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
@@ -20,6 +20,7 @@
         router.attach("/add/intents/json", IntentResource.class);
         router.attach("/get/intents/json", IntentResource.class);
         router.attach("/get/intent/{intent_id}/json", IntentResource.class);
+        router.attach("/get/ng-events/json", GetNGEventsResource.class);
         return router;
     }
 
diff --git a/src/main/java/net/onrc/onos/datagrid/web/GetNGEventsResource.java b/src/main/java/net/onrc/onos/datagrid/web/GetNGEventsResource.java
new file mode 100644
index 0000000..20efa68
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datagrid/web/GetNGEventsResource.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.datagrid.web;
+
+import java.util.Collection;
+
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.datagrid.IEventChannel;
+import net.onrc.onos.ofcontroller.networkgraph.TopologyEvent;
+import net.onrc.onos.ofcontroller.networkgraph.TopologyManager;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GetNGEventsResource extends ServerResource {
+
+	public static final Logger log = LoggerFactory.getLogger(GetNGEventsResource.class);
+	
+	@Get("json")
+	public String retrieve() {
+		IDatagridService datagridService = 
+				(IDatagridService) getContext().getAttributes().
+				get(IDatagridService.class.getCanonicalName());
+		
+		
+		log.debug("Get network graph events");
+		
+		IEventChannel<byte[], TopologyEvent> channel = datagridService.createChannel(TopologyManager.EVENT_CHANNEL_NAME, 
+				byte[].class, TopologyEvent.class);
+		
+		Collection<TopologyEvent> entries = channel.getAllEntries();
+		
+		String result = "";
+		for (TopologyEvent event : entries) {
+			result += event.toString() + "\n";
+		}
+		
+		return result;
+	}
+
+}
diff --git a/src/main/java/net/onrc/onos/intent/Intent.java b/src/main/java/net/onrc/onos/intent/Intent.java
index 9a40325..b6a51cb 100644
--- a/src/main/java/net/onrc/onos/intent/Intent.java
+++ b/src/main/java/net/onrc/onos/intent/Intent.java
@@ -1,5 +1,9 @@
 package net.onrc.onos.intent;
 
+import java.util.LinkedList;
+
+import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional;
+
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
@@ -18,19 +22,25 @@
 	private String id;
 	private IntentState state = IntentState.CREATED;
 
+	@Optional(value="logs")
+	private LinkedList<String> logs = new LinkedList<>();
+
 	/**
 	 * Default constructor for Kryo deserialization
 	 */
 	protected Intent() {
+		logs.add(String.format("created, time:%d", System.nanoTime())); // for measurement
 	}
 
 	public Intent(String id) {
+		logs.add(String.format("created, time:%d", System.nanoTime())); // for measurement
 		this.id = id;
 	}
 
 	public Intent(String id, IntentState state) {
+		logs.add(String.format("created, time:%d", System.nanoTime())); // for measurement
+		setState(state);
 		this.id = id;
-		this.state = state;
 	}
 
 	public String getId() {
@@ -42,11 +52,17 @@
 	}
 
 	public IntentState setState(IntentState newState) {
+		logs.add(String.format("setState, oldState:%s, newState:%s, time:%d",
+				state, newState, System.nanoTime())); // for measurement
 		IntentState oldState = state;
 		state = newState;
 		return oldState;
 	}
 
+	public LinkedList<String> getLogs() {
+		return logs;
+	}
+
 	@Override
 	public int hashCode() {
 		return (id == null) ? 0 : id.hashCode();
diff --git a/src/main/java/net/onrc/onos/intent/PathIntent.java b/src/main/java/net/onrc/onos/intent/PathIntent.java
index 28e2dd3..9a49035 100644
--- a/src/main/java/net/onrc/onos/intent/PathIntent.java
+++ b/src/main/java/net/onrc/onos/intent/PathIntent.java
@@ -11,7 +11,7 @@
 	protected Intent parentIntent;
 
 	public static String createFirstId(String parentId) {
-		return String.format("pi%s___0", parentId);
+		return String.format("%s___0", parentId);
 	}
 
 	public static String createNextId(String currentId) {
diff --git a/src/main/java/net/onrc/onos/intent/PathIntentMap.java b/src/main/java/net/onrc/onos/intent/PathIntentMap.java
index d70def2..85ea16c 100644
--- a/src/main/java/net/onrc/onos/intent/PathIntentMap.java
+++ b/src/main/java/net/onrc/onos/intent/PathIntentMap.java
@@ -7,22 +7,47 @@
 
 import net.onrc.onos.ofcontroller.networkgraph.Link;
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.PortEvent.SwitchPort;
 
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
 public class PathIntentMap extends IntentMap {
-	protected HashMap<LinkEvent, HashSet<PathIntent>> linkToIntents = new HashMap<>();
+	private HashMap<Long, HashMap<Long, HashSet<PathIntent>>> intents;
+
+	public PathIntentMap() {
+		intents = new HashMap<>();
+	}
+
+	private HashSet<PathIntent> get(SwitchPort swPort) {
+		Long dpid = swPort.getDpid();
+		Long port = swPort.getNumber();
+		HashMap<Long, HashSet<PathIntent>> portToIntents = intents.get(dpid);
+		if (portToIntents == null) {
+			portToIntents = new HashMap<>();
+			intents.put(dpid, portToIntents);
+		}
+		HashSet<PathIntent> targetIntents = portToIntents.get(port);
+		if (targetIntents == null) {
+			targetIntents = new HashSet<>();
+			portToIntents.put(port, targetIntents);
+		}
+		return targetIntents;
+	}
+
+	private void put(SwitchPort swPort, PathIntent intent) {
+		get(swPort).add(intent);
+	}
 
 	@Override
 	protected void putIntent(Intent intent) {
+		if (!(intent instanceof PathIntent)) return; // TODO throw exception
 		super.putIntent(intent);
-		for (LinkEvent linkEvent: ((PathIntent) intent).getPath()) {
-			HashSet<PathIntent> value = linkToIntents.get(linkEvent);
-			if (value == null)
-				value = new HashSet<PathIntent>();
-			value.add((PathIntent) intent);
-			linkToIntents.put(linkEvent, value);
+
+		PathIntent pathIntent = (PathIntent) intent;
+		for (LinkEvent linkEvent: pathIntent.getPath()) {
+			put(linkEvent.getSrc(), (PathIntent) intent);
+			put(linkEvent.getDst(), (PathIntent) intent);
 		}
 	}
 
@@ -30,20 +55,38 @@
 	protected void removeIntent(String intentId) {
 		PathIntent intent = (PathIntent) getIntent(intentId);
 		for (LinkEvent linkEvent: intent.getPath()) {
-			HashSet<PathIntent> value = linkToIntents.get(linkEvent);
-			value.remove(intent);
+			get(linkEvent.getSrc()).remove(intent);
+			get(linkEvent.getDst()).remove(intent);
 		}
 		super.removeIntent(intentId);
 	}
 
 	public Collection<PathIntent> getIntentsByLink(LinkEvent linkEvent) {
-		Collection<PathIntent> intents = linkToIntents.get(linkEvent);
-		if (intents == null) {
-			return null;
+		return getIntentsByPort(
+				linkEvent.getSrc().getDpid(),
+				linkEvent.getSrc().getNumber());
+	}
+
+	public Collection<PathIntent> getIntentsByPort(Long dpid, Long port) {
+		HashMap<Long, HashSet<PathIntent>> portToIntents = intents.get(dpid);
+		if (portToIntents != null) {
+			HashSet<PathIntent> targetIntents = portToIntents.get(port);
+			if (targetIntents != null) {
+				return Collections.unmodifiableCollection(targetIntents);
+			}
 		}
-		else {
-			return Collections.unmodifiableCollection(intents);
+		return new HashSet<>();
+	}
+
+	public Collection<PathIntent> getIntentsByDpid(Long dpid) {
+		HashSet<PathIntent> result = new HashSet<>();
+		HashMap<Long, HashSet<PathIntent>> portToIntents = intents.get(dpid);
+		if (portToIntents != null) {
+			for (HashSet<PathIntent> targetIntents: portToIntents.values()) {
+				result.addAll(targetIntents);
+			}
 		}
+		return result;
 	}
 
 	/**
@@ -55,7 +98,7 @@
 		if (link == null) return null;
 		Double bandwidth = link.getCapacity();
 		LinkEvent linkEvent = new LinkEvent(link);
-		if (!bandwidth.isInfinite() && linkToIntents.containsKey(linkEvent)) {
+		if (!bandwidth.isInfinite()) {
 			for (PathIntent intent: getIntentsByLink(linkEvent)) {
 				Double intentBandwidth = intent.getBandwidth();
 				if (intentBandwidth == null || intentBandwidth.isInfinite() || intentBandwidth.isNaN())
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
index 043a006..64d3def 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
@@ -42,6 +42,9 @@
 		IntentOperationList pathIntentOpList = new IntentOperationList();
 		HashMap<Switch, ConstrainedBFSTree> spfTrees = new HashMap<>();
 
+		// TODO optimize locking of NetworkGraph
+		graph.acquireReadLock();
+
 		for (IntentOperation intentOp: intentOpList) {
 			switch (intentOp.operator) {
 			case ADD:
@@ -119,6 +122,9 @@
 				break;
 			}
 		}
+		// TODO optimize locking of NetworkGraph
+		graph.releaseReadLock();
+
 		return pathIntentOpList;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
index 67902dd..2eecf2c 100755
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -8,6 +8,9 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -48,28 +51,24 @@
 	private IEventChannel<Long, IntentOperationList> opEventChannel;
 	private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
 	private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
+	private static final Logger log = LoggerFactory.getLogger(PathCalcRuntimeModule.class);
 
 	// ================================================================================
 	// private methods
 	// ================================================================================
 
-	private void reroutePaths(Collection<LinkEvent> removedLinkEvents) {
-		HashSet<PathIntent> oldPaths = new HashSet<>();
-		for (LinkEvent linkEvent : removedLinkEvents) {
-			Collection<PathIntent> intents = pathIntents.getIntentsByLink(linkEvent);
-			if (intents == null)
-				continue;
-			oldPaths.addAll(intents);
-		}
-
-		if (oldPaths.isEmpty())
+	private void reroutePaths(Collection<Intent> oldPaths) {
+		if (oldPaths == null || oldPaths.isEmpty())
 			return;
 
 		IntentOperationList reroutingOperation = new IntentOperationList();
-		for (PathIntent pathIntent : oldPaths) {
-			reroutingOperation.add(Operator.ADD, pathIntent.getParentIntent());
+		for (Intent pathIntent : oldPaths) {
+			reroutingOperation.add(Operator.ADD, ((PathIntent) pathIntent).getParentIntent());
 		}
-		executeIntentOperations(reroutingOperation);
+	}
+
+	private void log(String step) {
+		log.error("Step:{}, Time:{}", step, System.nanoTime());
 	}
 
 	// ================================================================================
@@ -85,14 +84,14 @@
 
 	@Override
 	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>(1);
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
 		m.put(IPathCalcRuntimeService.class, this);
 		return m;
 	}
 
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(2);
 		l.add(IDatagridService.class);
 		l.add(INetworkGraphService.class);
 		return l;
@@ -123,9 +122,12 @@
 	@Override
 	public IntentOperationList executeIntentOperations(IntentOperationList list) {
 		// update the map of high-level intents
+		log("begin_updateInMemoryIntents");
 		highLevelIntents.executeOperations(list);
+		log("end_updateInMemoryIntents");
 
 		// change states of high-level intents
+		log("begin_updateInMemoryIntents");
 		IntentStateList states = new IntentStateList();
 		for (IntentOperation op : list) {
 			String id = op.intent.getId();
@@ -135,15 +137,21 @@
 				states.put(id, IntentState.INST_REQ);
 		}
 		highLevelIntents.changeStates(states);
+		log("end_updateInMemoryIntents");
 
 		// calculate path-intents (low-level operations)
+		log("begin_calcPathIntents");
 		IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, pathIntents);
+		log("end_calcPathIntents");
 
 		// persist calculated low-level operations into data store
+		log("begin_persistPathIntents");
 		long key = persistIntent.getKey();
 		persistIntent.persistIfLeader(key, pathIntentOperations);
+		log("end_persistPathIntents");
 
 		// remove error-intents and reflect them to high-level intents
+		log("begin_removeErrorIntents");
 		states.clear();
 		Iterator<IntentOperation> i = pathIntentOperations.iterator();
 		while (i.hasNext()) {
@@ -154,19 +162,26 @@
 			}
 		}
 		highLevelIntents.changeStates(states);
+		log("end_removeErrorIntents");
 
-		// update the map of low-level intents and publish the low-level
-		// operations
+		// update the map of path intents and publish the path operations
+		log("begin_updateInMemoryPathIntents");
 		pathIntents.executeOperations(pathIntentOperations);
+		log("end_updateInMemoryPathIntents");
 
-		// send remove operation includes intent which has a complete path
-		// TODO need optimization
+		// Demo special: add a complete path to remove operation
+		log("begin_addPathToRemoveOperation");
 		for (IntentOperation op: pathIntentOperations) {
 			if(op.operator.equals(Operator.REMOVE)) {
 				op.intent = pathIntents.getIntent(op.intent.getId());
 			}
 		}
+		log("end_addPathToRemoveOperation");
+
+		// send notification
+		log("begin_sendNotification");
 		opEventChannel.addEntry(key, pathIntentOperations);
+		log("end_sendNotification");
 		return pathIntentOperations;
 	}
 
@@ -199,8 +214,35 @@
 			Collection<LinkEvent> removedLinkEvents,
 			Collection<DeviceEvent> addedDeviceEvents,
 			Collection<DeviceEvent> removedDeviceEvents) {
-		// TODO add getIntentsByPort() and getIntentsBySwitch() to PathIntentMap.
-		reroutePaths(removedLinkEvents);
+
+		log("called_networkGraphEvents");
+		HashSet<Intent> affectedPaths = new HashSet<>();
+
+		if (addedLinkEvents.size() > 0 ||
+				addedPortEvents.size() > 0 ||
+				addedSwitchEvents.size() > 0) {
+			log("begin_getAllIntents");
+			affectedPaths.addAll(getPathIntents().getAllIntents());
+			log("end_getAllIntents");
+		}
+		else {
+			log("begin_getIntentsByLink");
+			for (LinkEvent linkEvent: removedLinkEvents)
+				affectedPaths.addAll(pathIntents.getIntentsByLink(linkEvent));
+			log("end_getIntentsByLink");
+
+			log("begin_getIntentsByPort");
+			for (PortEvent portEvent: removedPortEvents)
+				affectedPaths.addAll(pathIntents.getIntentsByPort(portEvent.getDpid(), portEvent.getNumber()));
+			log("end_getIntentsByPort");
+
+			log("begin_getIntentsByDpid");
+			for (SwitchEvent switchEvent: removedSwitchEvents)
+				affectedPaths.addAll(pathIntents.getIntentsByDpid(switchEvent.getDpid()));
+			log("end_getIntentsByDpid");
+		}
+		reroutePaths(affectedPaths);
+		log("finished_networkGraphEvents");
 	}
 
 	// ================================================================================
@@ -209,6 +251,7 @@
 
 	@Override
 	public void entryAdded(IntentStateList value) {
+		log("called_EntryAdded");
 		entryUpdated(value);
 	}
 
@@ -219,7 +262,9 @@
 
 	@Override
 	public void entryUpdated(IntentStateList value) {
+		log("called_EntryUpdated");
 		// reflect state changes of path-level intent into application-level intents
+		log("begin_changeStateByNotification");
 		IntentStateList parentStates = new IntentStateList();
 		for (Entry<String, IntentState> entry: value.entrySet()) {
 			PathIntent pathIntent = (PathIntent) pathIntents.getIntent(entry.getKey());
@@ -245,5 +290,6 @@
 		}
 		highLevelIntents.changeStates(parentStates);
 		pathIntents.changeStates(value);
+		log("end_changeStateByNotification");
 	}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
index 7fe7955..ec3c0e4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
@@ -48,7 +48,7 @@
 	    .getLogger(TopologyManager.class);
 
     private IEventChannel<byte[], TopologyEvent> eventChannel;
-    private static final String EVENT_CHANNEL_NAME = "onos.topology";
+    public static final String EVENT_CHANNEL_NAME = "onos.topology";
     private EventHandler eventHandler = new EventHandler();
 
     private final NetworkGraphDatastore datastore;
@@ -387,6 +387,27 @@
 	    return;		// No events to dispatch
 	}
 
+	//
+	// Debug statements
+	// TODO: Those statements should be removed in the future
+	//
+	for (SwitchEvent switchEvent : apiAddedSwitchEvents)
+	    log.debug("Dispatch Network Graph Event: ADDED {}", switchEvent);
+	for (SwitchEvent switchEvent : apiRemovedSwitchEvents)
+	    log.debug("Dispatch Network Graph Event: REMOVED {}", switchEvent);
+	for (PortEvent portEvent : apiAddedPortEvents)
+	    log.debug("Dispatch Network Graph Event: ADDED {}", portEvent);
+	for (PortEvent portEvent : apiRemovedPortEvents)
+	    log.debug("Dispatch Network Graph Event: REMOVED {}", portEvent);
+	for (LinkEvent linkEvent : apiAddedLinkEvents)
+	    log.debug("Dispatch Network Graph Event: ADDED {}", linkEvent);
+	for (LinkEvent linkEvent : apiRemovedLinkEvents)
+	    log.debug("Dispatch Network Graph Event: REMOVED {}", linkEvent);
+	for (DeviceEvent deviceEvent : apiAddedDeviceEvents)
+	    log.debug("Dispatch Network Graph Event: ADDED {}", deviceEvent);
+	for (DeviceEvent deviceEvent : apiRemovedDeviceEvents)
+	    log.debug("Dispatch Network Graph Event: REMOVED {}", deviceEvent);
+
 	// Deliver the events
 	for (INetworkGraphListener listener : this.networkGraphListeners) {
 	    // TODO: Should copy before handing them over to listener?
@@ -433,16 +454,13 @@
 	//  - Apply reordered Link and Device Events if Switches or Ports
 	//    were added
 	//
-	Map<ByteBuffer, PortEvent> portEvents = reorderedAddedPortEvents;
-	Map<ByteBuffer, LinkEvent> linkEvents = reorderedAddedLinkEvents;
-	Map<ByteBuffer, DeviceEvent> deviceEvents = reorderedAddedDeviceEvents;
-	reorderedAddedPortEvents = new HashMap<>();
-	reorderedAddedLinkEvents = new HashMap<>();
-	reorderedAddedDeviceEvents = new HashMap<>();
+
 	//
 	// Apply reordered Port Events if Switches were added
 	//
 	if (hasAddedSwitchEvents) {
+	    Map<ByteBuffer, PortEvent> portEvents = reorderedAddedPortEvents;
+	    reorderedAddedPortEvents = new HashMap<>();
 	    for (PortEvent portEvent : portEvents.values())
 		addPort(portEvent);
 	}
@@ -450,8 +468,13 @@
 	// Apply reordered Link and Device Events if Switches or Ports
 	// were added.
 	//
+	Map<ByteBuffer, LinkEvent> linkEvents = reorderedAddedLinkEvents;
+	reorderedAddedLinkEvents = new HashMap<>();
 	for (LinkEvent linkEvent : linkEvents.values())
 	    addLink(linkEvent);
+	//
+	Map<ByteBuffer, DeviceEvent> deviceEvents = reorderedAddedDeviceEvents;
+	reorderedAddedDeviceEvents = new HashMap<>();
 	for (DeviceEvent deviceEvent : deviceEvents.values())
 	    addDevice(deviceEvent);
     }
@@ -1225,15 +1248,6 @@
 	//	}
 
 	for (RCLink l : RCLink.getAllLinks()) {
-	    // check if src/dst switch/port exist before triggering event
-	    Port srcPort = networkGraph.getPort(l.getSrc().dpid,
-						l.getSrc().number);
-	    Port dstPort = networkGraph.getPort(l.getDst().dpid,
-						l.getDst().number);
-	    if (srcPort == null || dstPort == null) {
-		continue;
-	    }
-
 	    LinkEvent linkEvent = new LinkEvent(l.getSrc().dpid,
 						l.getSrc().number,
 						l.getDst().dpid,
diff --git a/src/test/java/net/onrc/onos/intent/IntentTest.java b/src/test/java/net/onrc/onos/intent/IntentTest.java
new file mode 100644
index 0000000..abc5373
--- /dev/null
+++ b/src/test/java/net/onrc/onos/intent/IntentTest.java
@@ -0,0 +1,77 @@
+package net.onrc.onos.intent;
+
+import static org.junit.Assert.*;
+
+import java.util.HashSet;
+
+import org.junit.Test;
+
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
+public class IntentTest {
+	@Test
+	public void testCreateIntent() {
+		Intent intent = new Intent("id");
+		assertEquals("id", intent.getId());
+		assertEquals(Intent.IntentState.CREATED, intent.getState());
+	}
+
+	@Test
+	public void testCreateIntentWithState() {
+		Intent intent = new Intent("id", Intent.IntentState.INST_REQ);
+		assertEquals("id", intent.getId());
+		assertEquals(Intent.IntentState.INST_REQ, intent.getState());
+	}
+
+	@Test
+	public void testSetState() {
+		Intent intent = new Intent("id");
+
+		intent.setState(Intent.IntentState.INST_REQ);
+		assertEquals(Intent.IntentState.INST_REQ, intent.getState());
+
+		intent.setState(Intent.IntentState.DEL_REQ);
+		assertEquals(Intent.IntentState.DEL_REQ, intent.getState());
+	}
+
+	@Test
+	public void testEquals() {
+		Intent intent1 = new Intent("id1");
+		Intent intent2 = new Intent("id1");
+		Intent intent3 = new Intent("id2");
+		Intent intent4 = new Intent("id2");
+
+		assertEquals(intent1, intent2);
+		assertEquals(intent3, intent4);
+
+		assertFalse(intent1.equals(intent3));
+		assertFalse(intent3.equals(intent1));
+
+		intent1.setState(Intent.IntentState.INST_ACK);
+		intent2.setState(Intent.IntentState.INST_NACK);
+		assertEquals(intent1, intent2);
+	}
+
+	@Test
+	public void testHashCode() {
+		Intent intent1 = new Intent("id1");
+		intent1.setState(Intent.IntentState.INST_ACK);
+		Intent intent2 = new Intent("id1");
+		intent2.setState(Intent.IntentState.INST_NACK);
+		Intent intent3 = new Intent("id2");
+		Intent intent4 = new Intent("id2");
+
+		HashSet<Intent> intents = new HashSet<>();
+		intents.add(intent1);
+		intents.add(intent2);
+		intents.add(intent3);
+		intents.add(intent4);
+
+		assertEquals(2, intents.size());
+		assertTrue(intents.contains(intent1));
+		assertTrue(intents.contains(intent2));
+		assertTrue(intents.contains(intent3));
+		assertTrue(intents.contains(intent4));
+	}
+}
diff --git a/src/test/java/net/onrc/onos/intent/PathIntentMapTest.java b/src/test/java/net/onrc/onos/intent/PathIntentMapTest.java
new file mode 100644
index 0000000..1fb96ad
--- /dev/null
+++ b/src/test/java/net/onrc/onos/intent/PathIntentMapTest.java
@@ -0,0 +1,197 @@
+package net.onrc.onos.intent;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Collection;
+
+import net.onrc.onos.intent.IntentOperation.Operator;
+import net.onrc.onos.ofcontroller.networkgraph.Link;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.Path;
+import net.onrc.onos.ofcontroller.networkgraph.Port;
+import net.onrc.onos.ofcontroller.networkgraph.Switch;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PathIntentMapTest {
+	Link link12, link23, link24;
+	Switch sw1, sw2, sw3, sw4;
+	Port port11, port22, port21, port23, port31, port41;
+	Path path1, path2;
+	PathIntent intent1, intent2;
+
+	@Before
+	public void setUp() throws Exception {
+		sw1 = createMock(Switch.class);
+		sw2 = createMock(Switch.class);
+		sw3 = createMock(Switch.class);
+		sw4 = createMock(Switch.class);
+		expect(sw1.getDpid()).andReturn(1L).anyTimes();
+		expect(sw2.getDpid()).andReturn(2L).anyTimes();
+		expect(sw3.getDpid()).andReturn(3L).anyTimes();
+		expect(sw4.getDpid()).andReturn(4L).anyTimes();
+		replay(sw1);
+		replay(sw2);
+		replay(sw3);
+		replay(sw4);
+
+		port11 = createMock(Port.class);
+		port22 = createMock(Port.class);
+		port21 = createMock(Port.class);
+		port23 = createMock(Port.class);
+		port31 = createMock(Port.class);
+		port41 = createMock(Port.class);
+		expect(port11.getNumber()).andReturn(1L).anyTimes();
+		expect(port22.getNumber()).andReturn(2L).anyTimes();
+		expect(port21.getNumber()).andReturn(1L).anyTimes();
+		expect(port23.getNumber()).andReturn(3L).anyTimes();
+		expect(port31.getNumber()).andReturn(1L).anyTimes();
+		expect(port41.getNumber()).andReturn(1L).anyTimes();
+		replay(port11);
+		replay(port22);
+		replay(port21);
+		replay(port23);
+		replay(port31);
+		replay(port41);
+
+		link12 = createMock(Link.class);
+		link23 = createMock(Link.class);
+		link24 = createMock(Link.class);
+		expect(link12.getCapacity()).andReturn(1000.0).anyTimes();
+		expect(link23.getCapacity()).andReturn(1000.0).anyTimes();
+		expect(link24.getCapacity()).andReturn(1000.0).anyTimes();
+		expect(link12.getSrcSwitch()).andReturn(sw1).anyTimes();
+		expect(link23.getSrcSwitch()).andReturn(sw2).anyTimes();
+		expect(link24.getSrcSwitch()).andReturn(sw2).anyTimes();
+		expect(link12.getSrcPort()).andReturn(port11).anyTimes();
+		expect(link23.getSrcPort()).andReturn(port21).anyTimes();
+		expect(link24.getSrcPort()).andReturn(port23).anyTimes();
+		expect(link12.getDstSwitch()).andReturn(sw2).anyTimes();
+		expect(link23.getDstSwitch()).andReturn(sw3).anyTimes();
+		expect(link24.getDstSwitch()).andReturn(sw4).anyTimes();
+		expect(link12.getDstPort()).andReturn(port22).anyTimes();
+		expect(link23.getDstPort()).andReturn(port31).anyTimes();
+		expect(link24.getDstPort()).andReturn(port41).anyTimes();
+		replay(link12);
+		replay(link23);
+		replay(link24);
+
+		path1 = new Path();
+		path1.add(new LinkEvent(link12));
+		path1.add(new LinkEvent(link23));
+
+		path2 = new Path();
+		path2.add(new LinkEvent(link12));
+		path2.add(new LinkEvent(link24));
+
+		intent1 = new PathIntent("1", path1, 400.0, new Intent("_1"));
+		intent2 = new PathIntent("2", path2, 400.0, new Intent("_2"));
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testCreate() {
+		PathIntentMap intents = new PathIntentMap();
+		assertEquals(0, intents.getAllIntents().size());
+	}
+
+	@Test
+	public void testGetIntentsByDpid() {
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		assertEquals(2, operations.size());
+
+		PathIntentMap intents = new PathIntentMap();
+		intents.executeOperations(operations);
+		assertEquals(2, intents.getAllIntents().size());
+
+		Collection<PathIntent> pathIntents = intents.getIntentsByDpid(1L);
+		assertEquals(2, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+		assertTrue(pathIntents.contains(intent2));
+
+		pathIntents = intents.getIntentsByDpid(2L);
+		assertEquals(2, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+		assertTrue(pathIntents.contains(intent2));
+
+		pathIntents = intents.getIntentsByDpid(3L);
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+
+		pathIntents = intents.getIntentsByDpid(4L);
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent2));
+	}
+
+	@Test
+	public void testGetPathIntentsByPort() {
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		assertEquals(2, operations.size());
+
+		PathIntentMap intents = new PathIntentMap();
+		intents.executeOperations(operations);
+		assertEquals(2, intents.getAllIntents().size());
+
+		Collection<PathIntent> pathIntents = intents.getIntentsByPort(1L, 1L);
+		assertEquals(2, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+		assertTrue(pathIntents.contains(intent2));
+
+		pathIntents = intents.getIntentsByPort(2L, 1L);
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+
+		pathIntents = intents.getIntentsByPort(2L, 3L);
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent2));
+	}
+
+	@Test
+	public void testGetPathIntentsByLink() {
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		assertEquals(2, operations.size());
+
+		PathIntentMap intents = new PathIntentMap();
+		intents.executeOperations(operations);
+		assertEquals(2, intents.getAllIntents().size());
+
+		Collection<PathIntent> pathIntents = intents.getIntentsByLink(new LinkEvent(link12));
+		assertEquals(2, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+		assertTrue(pathIntents.contains(intent2));
+
+		pathIntents = intents.getIntentsByLink(new LinkEvent(link23));
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent1));
+
+		pathIntents = intents.getIntentsByLink(new LinkEvent(link24));
+		assertEquals(1, pathIntents.size());
+		assertTrue(pathIntents.contains(intent2));
+	}
+
+	@Test
+	public void testGetAvailableBandwidth() {
+		IntentOperationList operations = new IntentOperationList();
+		operations.add(Operator.ADD, intent1);
+		operations.add(Operator.ADD, intent2);
+		assertEquals(2, operations.size());
+
+		PathIntentMap intents = new PathIntentMap();
+		intents.executeOperations(operations);
+		assertEquals(2, intents.getAllIntents().size());
+
+		assertEquals(200.0, intents.getAvailableBandwidth(link12), 0.0);
+	}
+}
diff --git a/src/test/java/net/onrc/onos/intent/PathIntentTest.java b/src/test/java/net/onrc/onos/intent/PathIntentTest.java
index 211a68a..dd34e90 100644
--- a/src/test/java/net/onrc/onos/intent/PathIntentTest.java
+++ b/src/test/java/net/onrc/onos/intent/PathIntentTest.java
@@ -34,13 +34,13 @@
 	@Test
 	public void testCreateFirstId() {
 		String id = PathIntent.createFirstId("100");
-		assertEquals("pi100___0", id);
+		assertEquals("100___0", id);
 	}
 
 	@Test
 	public void testCreateNextId() {
-		String id = PathIntent.createNextId("pi100___999");
-		assertEquals("pi100___1000", id);
+		String id = PathIntent.createNextId("100___999");
+		assertEquals("100___1000", id);
 	}
 
 	@Test
diff --git a/web/get_datagrid_ngevents.py b/web/get_datagrid_ngevents.py
new file mode 100755
index 0000000..34c3338
--- /dev/null
+++ b/web/get_datagrid_ngevents.py
@@ -0,0 +1,63 @@
+#! /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/onos/datagrid/get/map/<map-name>/json ")
+# Sample output:
+
+def print_datagrid_map(parsedResult):
+  print '%s' % (parsedResult)
+
+def get_datagrid_map():
+  try:
+    command = "curl -s \"http://%s:%s/wm/onos/datagrid/get/ng-events/json\"" % (ControllerIP, ControllerPort)
+    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__":
+  
+  # Do the work
+  get_datagrid_map()
diff --git a/web/topology_rest_napi.py b/web/topology_rest_napi.py
new file mode 100755
index 0000000..45fc814
--- /dev/null
+++ b/web/topology_rest_napi.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+import json
+from urllib2 import Request, urlopen, URLError, HTTPError
+from flask import Flask, json, Response, render_template, make_response, request
+
+## Global Var for ON.Lab local REST ##
+RestIP="localhost"
+RestPort=8080
+ONOS_DEFAULT_HOST="localhost" ;# Has to set if LB=False
+DEBUG=1
+controllers=["ubuntu1","ubuntu2","ubuntu3","ubuntu4"]
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+  print '%s' % (txt)
+
+def debug(txt):
+  if DEBUG:
+    print '%s' % (txt)
+
+def node_id(switch_array, dpid):
+  id = -1
+  for i, val in enumerate(switch_array):
+    if val['name'] == dpid:
+      id = i
+      break
+
+  return id
+
+###### ONOS REST API ##############################
+## Worker Func ###
+def get_json(url):
+  code = 200;
+  try:
+    response = urlopen(url)
+  except URLError, e:
+    print "get_json: REST IF %s has issue. Reason: %s" % (url, e.reason)
+    result = ""
+    return (500, result)
+  except HTTPError, e:
+    print "get_json: REST IF %s has issue. Code %s" % (url, e.code)
+    result = ""
+    return (e.code, result)
+
+  result = response.read()
+#  parsedResult = json.loads(result)
+  return (code, result)
+
+### File Fetch ###
+@app.route('/ui/img/<filename>', methods=['GET'])
+@app.route('/img/<filename>', methods=['GET'])
+@app.route('/css/<filename>', methods=['GET'])
+@app.route('/js/models/<filename>', methods=['GET'])
+@app.route('/js/views/<filename>', methods=['GET'])
+@app.route('/js/<filename>', methods=['GET'])
+@app.route('/lib/<filename>', methods=['GET'])
+@app.route('/log/<filename>', methods=['GET'])
+@app.route('/', methods=['GET'])
+@app.route('/<filename>', methods=['GET'])
+@app.route('/tpl/<filename>', methods=['GET'])
+@app.route('/ons-demo/<filename>', methods=['GET'])
+@app.route('/ons-demo/js/<filename>', methods=['GET'])
+@app.route('/ons-demo/d3/<filename>', methods=['GET'])
+@app.route('/ons-demo/css/<filename>', methods=['GET'])
+@app.route('/ons-demo/assets/<filename>', methods=['GET'])
+@app.route('/ons-demo/data/<filename>', methods=['GET'])
+def return_file(filename="index.html"):
+  if request.path == "/":
+    fullpath = "./index.html"
+  else:
+    fullpath = str(request.path)[1:]
+
+  try:
+    open(fullpath)
+  except:
+    response = make_response("Cannot find a file: %s" % (fullpath), 500)
+    response.headers["Content-type"] = "text/html"
+    return response
+
+  response = make_response(open(fullpath).read())
+  suffix = fullpath.split(".")[-1]
+
+  if suffix == "html" or suffix == "htm":
+    response.headers["Content-type"] = "text/html"
+  elif suffix == "js":
+    response.headers["Content-type"] = "application/javascript"
+  elif suffix == "css":
+    response.headers["Content-type"] = "text/css"
+  elif suffix == "png":
+    response.headers["Content-type"] = "image/png"
+  elif suffix == "svg":
+    response.headers["Content-type"] = "image/svg+xml"
+
+  return response
+
+## API for ON.Lab local GUI ##
+@app.route('/topology', methods=['GET'])
+def topology_for_gui():
+  try:
+    #url="http://%s:%s/wm/onos/topology/switches/all/json" % (RestIP, RestPort)
+    url="http://%s:%s/wm/onos/ng/switches/json" % (RestIP, RestPort)
+    (code, result) = get_json(url)
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % url)
+    log_error("%s" % result)
+    return
+
+  topo = {}
+  switches = []
+  links = []
+  devices = []
+
+  for v in parsedResult:
+    if v.has_key('dpid'):
+#      if v.has_key('dpid') and str(v['state']) == "ACTIVE":#;if you want only ACTIVE nodes
+      dpid = str(v['dpid'])
+      state = str(v['state'])
+      sw = {}
+      sw['name']=dpid
+      sw['group']= -1
+
+      if state == "INACTIVE":
+        sw['group']=0
+      switches.append(sw)
+
+  try:
+    url="http://%s:%s/wm/onos/registry/switches/json" % (RestIP, RestPort)
+    (code, result) = get_json(url)
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % url)
+    log_error("%s" % result)
+
+  for key in parsedResult:
+    dpid = key
+    ctrl = parsedResult[dpid][0]['controllerId']
+    sw_id = node_id(switches, dpid)
+    if sw_id != -1:
+      if switches[sw_id]['group'] != 0:
+        switches[sw_id]['group'] = controllers.index(ctrl) + 1
+
+  try:
+    #url = "http://%s:%s/wm/onos/topology/links/json" % (RestIP, RestPort)
+    url = "http://%s:%s/wm/onos/ng/links/json" % (RestIP, RestPort)
+    (code, result) = get_json(url)
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % url)
+    log_error("%s" % result)
+    return
+#    sys.exit(0)
+
+  for v in parsedResult:
+    link = {}
+    if v.has_key('dst-switch'):
+      dst_dpid = str(v['dst-switch'])
+      dst_id = node_id(switches, dst_dpid)
+    if v.has_key('src-switch'):
+      src_dpid = str(v['src-switch'])
+      src_id = node_id(switches, src_dpid)
+    link['source'] = src_id
+    link['target'] = dst_id
+
+    #onpath = 0
+    #for (s,d) in path:
+    #  if s == v['src-switch'] and d == v['dst-switch']:
+    #    onpath = 1
+    #    break
+    #link['type'] = onpath
+
+    links.append(link)
+
+  topo['nodes'] = switches
+  topo['links'] = links
+
+  js = json.dumps(topo)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+@app.route("/controller_status")
+def controller_status():
+  url= "http://%s:%d/wm/onos/registry/controllers/json" % (RestIP, RestPort)
+  (code, result) = get_json(url)
+  parsedResult = json.loads(result)
+
+  cont_status=[]
+  for i in controllers:
+    status={}
+    if i in parsedResult:
+      onos=1
+    else:
+      onos=0
+    status["name"]=i
+    status["onos"]=onos
+    status["cassandra"]=0
+    cont_status.append(status)
+
+  js = json.dumps(cont_status)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+if __name__ == "__main__":
+  app.debug = True
+  app.run(threaded=True, host="0.0.0.0", port=9000)