Update rerouting features

- add REROUTE_REQ state to Intent class
- add static methods to PathIntent class to create IDs
- add getPathIntentId() to ShortestPathIntent class to retrieve low-level intent
- add mapping of inserting or replacing high level ids to lower level ids.
- change high level intent state to REROUTE_REQ if the state was INST_ACK and the intent was requested rerouting

Change-Id: I50970a0255e2b19451aaedb7dc3c19015031e5d0
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 201e97d..043a006 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
@@ -8,6 +8,7 @@
 import net.onrc.onos.intent.ErrorIntent;
 import net.onrc.onos.intent.ErrorIntent.ErrorType;
 import net.onrc.onos.intent.Intent;
+import net.onrc.onos.intent.Intent.IntentState;
 import net.onrc.onos.intent.IntentOperation;
 import net.onrc.onos.intent.IntentOperation.Operator;
 import net.onrc.onos.intent.IntentOperationList;
@@ -63,13 +64,13 @@
 					pathIntentOpList.add(Operator.ERROR, new ErrorIntent(
 							ErrorType.SWITCH_NOT_FOUND,
 							"Switch not found.",
-							intentOp.intent));
+							spIntent));
 					continue;
 				}
 
 				double bandwidth = 0.0;
 				ConstrainedBFSTree tree = null;
-				if (intentOp.intent instanceof ConstrainedShortestPathIntent) {
+				if (spIntent instanceof ConstrainedShortestPathIntent) {
 					bandwidth = ((ConstrainedShortestPathIntent) intentOp.intent).getBandwidth();
 					tree = new ConstrainedBFSTree(srcSwitch, pathIntents, bandwidth);
 				}
@@ -82,18 +83,36 @@
 				}
 				Path path = tree.getPath(dstSwitch);
 				if (path == null) {
-					log.error("Path not found: {}", intentOp.intent.toString());
+					log.error("Path not found: {}", spIntent.toString());
 					pathIntentOpList.add(Operator.ERROR, new ErrorIntent(
 							ErrorType.PATH_NOT_FOUND,
 							"Path not found.",
-							intentOp.intent));
+							spIntent));
 					continue;
 				}
-				PathIntent pathIntent = new PathIntent("pi" + intentOp.intent.getId(), path, bandwidth, intentOp.intent);
+
+				// generate new path-intent ID
+				String oldPathIntentId = spIntent.getPathIntentId();
+				String newPathIntentId;
+				if (oldPathIntentId == null)
+					newPathIntentId = PathIntent.createFirstId(spIntent.getId());
+				else {
+					newPathIntentId = PathIntent.createNextId(oldPathIntentId);
+
+					// Request removal of low-level intent if it exists.
+					pathIntentOpList.add(Operator.REMOVE, new Intent(oldPathIntentId));
+				}
+
+				// create new path-intent
+				PathIntent pathIntent = new PathIntent(newPathIntentId, path, bandwidth, spIntent);
+				pathIntent.setState(IntentState.INST_REQ);
+				spIntent.setPathIntent(pathIntent);
 				pathIntentOpList.add(Operator.ADD, pathIntent);
+
 				break;
 			case REMOVE:
-				pathIntentOpList.add(Operator.REMOVE, new Intent("pi" + intentOp.intent.getId()));
+				pathIntentOpList.add(Operator.REMOVE, new Intent(
+						((ShortestPathIntent) intentOp.intent).getPathIntentId()));
 				break;
 			case ERROR:
 				// just ignore
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 6bdca51..b5521e2 100755
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -13,7 +13,6 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.datagrid.IEventChannel;
-import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.Intent.IntentState;
 import net.onrc.onos.intent.IntentMap;
 import net.onrc.onos.intent.IntentOperation;
@@ -60,11 +59,9 @@
 
 		if (oldPaths.isEmpty())
 			return;
+
 		IntentOperationList reroutingOperation = new IntentOperationList();
 		for (PathIntent pathIntent : oldPaths) {
-			// TODO use Operator.UPDATE instead of REMOVE and ADD in order to
-			// optimize
-			reroutingOperation.add(Operator.REMOVE, new Intent(pathIntent.getParentIntent().getId()));
 			reroutingOperation.add(Operator.ADD, pathIntent.getParentIntent());
 		}
 		executeIntentOperations(reroutingOperation);
@@ -123,21 +120,26 @@
 		// update the map of high-level intents
 		highLevelIntents.executeOperations(list);
 
-		// prepare high-level intents' state changes
+		// change states of high-level intents
 		HashMap<String, IntentState> states = new HashMap<>();
 		for (IntentOperation op : list) {
 			String id = op.intent.getId();
-			states.put(id, IntentState.INST_REQ);
+			if (op.intent.getState().equals(IntentState.INST_ACK))
+				states.put(id, IntentState.REROUTE_REQ);
+			else
+				states.put(id, IntentState.INST_REQ);
 		}
+		highLevelIntents.changeStates(states);
 
 		// calculate path-intents (low-level operations)
 		IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, pathIntents);
 
-		// persist calculated low-level operations
+		// persist calculated low-level operations into data store
 		long key = persistIntent.getKey();
 		persistIntent.persistIfLeader(key, pathIntentOperations);
 
 		// remove error-intents and reflect them to high-level intents
+		states.clear();
 		Iterator<IntentOperation> i = pathIntentOperations.iterator();
 		while (i.hasNext()) {
 			IntentOperation op = i.next();
@@ -185,7 +187,7 @@
 			Collection<DeviceEvent> addedDeviceEvents,
 			Collection<DeviceEvent> removedDeviceEvents) {
 		// TODO need optimization.
-		// This reroutes only if when some links are removed for now.
+		// Only high-level intents that affected by link down are rerouted.
 		reroutePaths(removedLinkEvents);
 	}
 }
\ No newline at end of file