Add intent error handling features.

- added ErrorIntent class to express intent processing errors
- added ERROR to IntentOperation.Operator enum
- made IntentMap to change intents' state automatically when it processes ErrorIntent instances
- made PathCalcRuntime to create ErrorIntent instance for failed intents
- made PathCalcRuntimeModule to process ErrorIntent and reflect them to high-level intents' states

Change-Id: Ib0cf9c284a37812695864d75d3cde2499a76e2a7
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 5600e0c..201e97d 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
@@ -5,6 +5,8 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.onrc.onos.intent.ConstrainedBFSTree;
 import net.onrc.onos.intent.ConstrainedShortestPathIntent;
+import net.onrc.onos.intent.ErrorIntent;
+import net.onrc.onos.intent.ErrorIntent.ErrorType;
 import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.IntentOperation;
 import net.onrc.onos.intent.IntentOperation.Operator;
@@ -33,9 +35,9 @@
 	 * calculate shortest-path and constrained-shortest-path intents into low-level path intents
 	 * @param intentOpList IntentOperationList having instances of ShortestPathIntent/ConstrainedShortestPathIntent
 	 * @param pathIntents a set of current low-level intents
-	 * @return IntentOperationList for PathIntent instances
+	 * @return IntentOperationList. PathIntent and/or ErrorIntent instances.
 	 */
-	public IntentOperationList calcPathIntents(IntentOperationList intentOpList, PathIntentMap pathIntents) {
+	public IntentOperationList calcPathIntents(final IntentOperationList intentOpList, final PathIntentMap pathIntents) {
 		IntentOperationList pathIntentOpList = new IntentOperationList();
 		HashMap<Switch, ConstrainedBFSTree> spfTrees = new HashMap<>();
 
@@ -43,8 +45,11 @@
 			switch (intentOp.operator) {
 			case ADD:
 				if (!(intentOp.intent instanceof ShortestPathIntent)) {
-					log.error("unsupported intent type: {}", intentOp.intent.getClass().getName());
-					// TODO should push back the intent to caller
+					log.error("Unsupported intent type: {}", intentOp.intent.getClass().getName());
+					pathIntentOpList.add(Operator.ERROR, new ErrorIntent(
+							ErrorType.UNSUPPORTED_INTENT,
+							"Unsupported intent type.",
+							intentOp.intent));
 					continue;
 				}
 
@@ -55,7 +60,10 @@
 					log.error("Switch not found: {}, {}",
 							spIntent.getSrcSwitchDpid(),
 							spIntent.getDstSwitchDpid());
-					// TODO should push back the intent to caller
+					pathIntentOpList.add(Operator.ERROR, new ErrorIntent(
+							ErrorType.SWITCH_NOT_FOUND,
+							"Switch not found.",
+							intentOp.intent));
 					continue;
 				}
 
@@ -75,15 +83,21 @@
 				Path path = tree.getPath(dstSwitch);
 				if (path == null) {
 					log.error("Path not found: {}", intentOp.intent.toString());
-					// TODO should push back the intent to caller
+					pathIntentOpList.add(Operator.ERROR, new ErrorIntent(
+							ErrorType.PATH_NOT_FOUND,
+							"Path not found.",
+							intentOp.intent));
 					continue;
 				}
 				PathIntent pathIntent = new PathIntent("pi" + intentOp.intent.getId(), path, bandwidth, intentOp.intent);
-				pathIntentOpList.add(new IntentOperation(IntentOperation.Operator.ADD, pathIntent));
+				pathIntentOpList.add(Operator.ADD, pathIntent);
 				break;
 			case REMOVE:
 				pathIntentOpList.add(Operator.REMOVE, new Intent("pi" + intentOp.intent.getId()));
 				break;
+			case ERROR:
+				// just ignore
+				break;
 			}
 		}
 		return pathIntentOpList;
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 3a9c607..df927f6 100755
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -3,6 +3,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -12,18 +13,20 @@
 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;
 import net.onrc.onos.intent.IntentOperation.Operator;
 import net.onrc.onos.intent.IntentOperationList;
 import net.onrc.onos.intent.PathIntent;
 import net.onrc.onos.intent.PathIntentMap;
+import net.onrc.onos.intent.persist.PersistIntent;
 import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphListener;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
 import net.onrc.onos.ofcontroller.networkgraph.PortEvent;
 import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
-import net.onrc.onos.intent.persist.PersistIntent;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 
 /**
@@ -93,18 +96,42 @@
 				IntentOperationList.class);
 		networkGraphService.registerNetworkGraphListener(this);
                 persistIntent = new PersistIntent(controllerRegistry, networkGraphService);
-                
+
 	}
 
 	@Override
 	public IntentOperationList executeIntentOperations(IntentOperationList list) {
+		// update the map of high-level intents
 		highLevelIntents.executeOperations(list);
+
+		// prepare high-level intents' state changes
+		HashMap<String, IntentState> states = new HashMap<>();
+		for (IntentOperation op: list) {
+			String id = op.intent.getId();
+			states.put(id, IntentState.INST_REQ);
+		}
+
+		// calculate path-intents (low-level operations)
 		IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, pathIntents);
+
+		// persist calculated low-level operations
 		long key = persistIntent.getKey();
-		System.out.println(pathIntentOperations);
+		persistIntent.persistIfLeader(key, pathIntentOperations);
+
+		// remove error-intents and reflect them to high-level intents
+		Iterator<IntentOperation> i = pathIntentOperations.iterator();
+		while (i.hasNext()) {
+			IntentOperation op = i.next();
+			if (op.operator.equals(Operator.ERROR)) {
+				states.put(op.intent.getId(), IntentState.INST_NACK);
+				i.remove();
+			}
+		}
+		highLevelIntents.changeStates(states);
+
+		// update the map of low-level intents and publish the low-level operations
 		pathIntents.executeOperations(pathIntentOperations);
 		eventChannel.addEntry(key, pathIntentOperations);
-                persistIntent.persistIfLeader(key, pathIntentOperations);
 		return pathIntentOperations;
 	}