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/ErrorIntent.java b/src/main/java/net/onrc/onos/intent/ErrorIntent.java
new file mode 100644
index 0000000..3365350
--- /dev/null
+++ b/src/main/java/net/onrc/onos/intent/ErrorIntent.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.intent;
+
+/**
+ * This class is instantiated by Run-times to express intent calculation error
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
+public class ErrorIntent extends Intent {
+ public enum ErrorType {
+ UNSUPPORTED_INTENT,
+ SWITCH_NOT_FOUND,
+ PATH_NOT_FOUND,
+ }
+
+ public ErrorType errorType;
+ public String message;
+ public Intent parentIntent;
+
+ /**
+ * Default constructor for Kryo deserialization
+ */
+ protected ErrorIntent() {
+ }
+
+ public ErrorIntent(ErrorType errorType, String message, Intent parentIntent) {
+ this.id = parentIntent.getId();
+ this.errorType = errorType;
+ this.message = message;
+ this.parentIntent = parentIntent;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/intent/IntentMap.java b/src/main/java/net/onrc/onos/intent/IntentMap.java
index 4657706..53667ad 100644
--- a/src/main/java/net/onrc/onos/intent/IntentMap.java
+++ b/src/main/java/net/onrc/onos/intent/IntentMap.java
@@ -5,6 +5,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Map.Entry;
import net.onrc.onos.intent.Intent.IntentState;
@@ -13,6 +14,10 @@
* @author Toshio Koide (t-koide@onlab.us)
*/
public class IntentMap {
+ private HashSet<ChangedListener> listeners = new HashSet<>();
+ private HashMap<String, Intent> intents = new HashMap<>();
+ private LinkedList<ChangedEvent> events = new LinkedList<>();
+
public enum ChangedEventType {
/**
* Added new intent.
@@ -47,47 +52,25 @@
void intentsChange(LinkedList<ChangedEvent> events);
}
- private HashSet<ChangedListener> listeners = new HashSet<>();
- private HashMap<String, Intent> intents = new HashMap<>();
-
- protected void putIntent(Intent intent) {
- if (intents.containsKey(intent.getId()))
- removeIntent(intent.getId());
- intents.put(intent.getId(), intent);
- }
-
- protected void removeIntent(String intentId) {
- intents.remove(intentId);
- }
-
- public Intent getIntent(String intentId) {
- return intents.get(intentId);
- }
+ //================================================================================
+ // public methods
+ //================================================================================
public void executeOperations(IntentOperationList operations) {
- LinkedList<ChangedEvent> events = new LinkedList<>();
for (IntentOperation operation: operations) {
switch (operation.operator) {
case ADD:
- putIntent(operation.intent);
- events.add(new ChangedEvent(ChangedEventType.ADDED, operation.intent));
+ handleAddOperation(operation);
break;
case REMOVE:
- Intent intent = getIntent(operation.intent.getId());
- if (intent == null) {
- // TODO throw exception
- }
- else {
- intent.setState(Intent.IntentState.DEL_REQ);
- events.add(new ChangedEvent(ChangedEventType.STATE_CHANGED,
- new Intent(intent.getId(), Intent.IntentState.DEL_REQ)));
- }
+ handleRemoveOperation(operation);
+ break;
+ case ERROR:
+ handleErrorOperation(operation);
break;
}
}
- for (ChangedListener listener: listeners) {
- listener.intentsChange(events);
- }
+ notifyEvents();
}
public void purge() {
@@ -97,11 +80,23 @@
if (intent.getState() == IntentState.DEL_ACK
|| intent.getState() == IntentState.INST_NACK) {
removeIds.add(intent.getId());
- }
+ }
}
for (String intentId: removeIds) {
removeIntent(intentId);
}
+ notifyEvents();
+ }
+
+ public void changeStates(Map<String, IntentState> states) {
+ for (Entry<String, IntentState> state: states.entrySet()) {
+ setState(state.getKey(), state.getValue());
+ }
+ notifyEvents();
+ }
+
+ public Intent getIntent(String intentId) {
+ return intents.get(intentId);
}
public Collection<Intent> getAllIntents() {
@@ -115,4 +110,80 @@
public void removeChangedListener(ChangedListener listener) {
listeners.remove(listener);
}
+
+ //================================================================================
+ // methods that affect intents map (protected)
+ //================================================================================
+
+ protected void putIntent(Intent intent) {
+ if (intents.containsKey(intent.getId()))
+ removeIntent(intent.getId());
+ intents.put(intent.getId(), intent);
+ events.add(new ChangedEvent(ChangedEventType.ADDED, intent));
+ }
+
+ protected void removeIntent(String intentId) {
+ Intent intent = intents.remove(intentId);
+ if (intent == null) return;
+ events.add(new ChangedEvent(ChangedEventType.REMOVED, intent));
+ }
+
+ protected void setState(String intentId, IntentState state) {
+ Intent intent = intents.get(intentId);
+ if (intent == null) return;
+ intent.setState(state);
+ events.add(new ChangedEvent(ChangedEventType.STATE_CHANGED, intent));
+ }
+
+ //================================================================================
+ // helper methods (protected)
+ //================================================================================
+
+ protected void handleAddOperation(IntentOperation operation) {
+ putIntent(operation.intent);
+ }
+
+ protected void handleRemoveOperation(IntentOperation operation) {
+ Intent intent = getIntent(operation.intent.getId());
+ if (intent == null) {
+ // TODO error handling
+ }
+ else {
+ setState(intent.getId(), IntentState.DEL_REQ);
+ }
+ }
+
+ protected void handleErrorOperation(IntentOperation operation) {
+ //TODO put error message into the intent
+
+ ErrorIntent errorIntent = (ErrorIntent) operation.intent;
+ Intent targetIntent = intents.get(errorIntent.getId());
+ if (targetIntent == null) {
+ // TODO error handling
+ return;
+ }
+
+ switch (targetIntent.getState()) {
+ case CREATED:
+ case INST_REQ:
+ case INST_ACK:
+ setState(targetIntent.getId(), IntentState.INST_NACK);
+ break;
+ case DEL_REQ:
+ setState(targetIntent.getId(), IntentState.DEL_PENDING);
+ break;
+ case INST_NACK:
+ case DEL_PENDING:
+ case DEL_ACK:
+ // do nothing
+ break;
+ }
+ }
+
+ protected void notifyEvents() {
+ for (ChangedListener listener: listeners) {
+ listener.intentsChange(events);
+ }
+ events.clear();
+ }
}
diff --git a/src/main/java/net/onrc/onos/intent/IntentOperation.java b/src/main/java/net/onrc/onos/intent/IntentOperation.java
index 506dd52..93a9f88 100644
--- a/src/main/java/net/onrc/onos/intent/IntentOperation.java
+++ b/src/main/java/net/onrc/onos/intent/IntentOperation.java
@@ -6,15 +6,21 @@
public class IntentOperation {
public enum Operator {
/**
- * Add new intent specified by intent field
+ * Add new intent specified by intent field.
*/
ADD,
/**
* Remove existing intent specified by intent field.
- * The specified intent should be an instance of Intent class (not a child class)
+ * The instance of intent field should be an instance of Intent class (not a child class)
*/
REMOVE,
+
+ /**
+ * Do error handling.
+ * The instance of intent field should be an instance of ErrorIntent
+ */
+ ERROR,
}
public IntentOperation() {}
diff --git a/src/main/java/net/onrc/onos/intent/IntentOperationList.java b/src/main/java/net/onrc/onos/intent/IntentOperationList.java
index 05e5b5c..881120a 100644
--- a/src/main/java/net/onrc/onos/intent/IntentOperationList.java
+++ b/src/main/java/net/onrc/onos/intent/IntentOperationList.java
@@ -1,11 +1,11 @@
package net.onrc.onos.intent;
-import java.util.ArrayList;
+import java.util.LinkedList;
/**
* @author Toshio Koide (t-koide@onlab.us)
*/
-public class IntentOperationList extends ArrayList<IntentOperation> {
+public class IntentOperationList extends LinkedList<IntentOperation> {
private static final long serialVersionUID = -3894081461861052610L;
public boolean add(IntentOperation.Operator op, Intent intent) {
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;
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
index f65a830..07b173d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
@@ -6,6 +6,7 @@
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.intent.ConstrainedShortestPathIntent;
+import net.onrc.onos.intent.ErrorIntent;
import net.onrc.onos.intent.Intent;
import net.onrc.onos.intent.IntentOperation;
import net.onrc.onos.intent.IntentOperationList;
@@ -179,6 +180,7 @@
kryo.register(IntentOperation.class);
kryo.register(PathIntent.class);
kryo.register(Intent.class);
+ kryo.register(ErrorIntent.class);
kryo.register(ShortestPathIntent.class);
kryo.register(ConstrainedShortestPathIntent.class);
kryo.register(Intent.IntentState.class);