Reworked intent states to the new set of states.
Separate intent state from intent event type.
Implemented new state transitions in IntentManager.
Implemented ObjectiveTracker.
Re-route now works.
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
index 541a702..de61e8e 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -71,11 +71,11 @@
private Intent createPathIntent(Path path, Host src, Host dst,
HostToHostIntent intent) {
- TrafficSelector selector = builder(intent.getTrafficSelector())
+ TrafficSelector selector = builder(intent.selector())
.matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
return new PathIntent(intentIdGenerator.getNewId(),
- selector, intent.getTrafficTreatment(),
+ selector, intent.treatment(),
path.src(), path.dst(), path);
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
index 6268245..197c2b2 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -28,11 +28,15 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.onos.net.intent.IntentState.*;
+import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -56,6 +60,8 @@
private final AbstractListenerRegistry<IntentEvent, IntentListener>
listenerRegistry = new AbstractListenerRegistry<>();
+ private ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
+
private final IntentStoreDelegate delegate = new InternalStoreDelegate();
private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
@@ -63,7 +69,7 @@
protected IntentStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowTrackerService trackerService;
+ protected ObjectiveTrackerService trackerService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@@ -89,21 +95,16 @@
checkNotNull(intent, INTENT_NULL);
registerSubclassCompilerIfNeeded(intent);
IntentEvent event = store.createIntent(intent);
- processStoreEvent(event);
+ if (event != null) {
+ eventDispatcher.post(event);
+ executor.execute(new IntentTask(COMPILING, intent));
+ }
}
@Override
public void withdraw(Intent intent) {
checkNotNull(intent, INTENT_NULL);
- IntentEvent event = store.setState(intent, WITHDRAWING);
- List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
- if (installables != null) {
- for (InstallableIntent installable : installables) {
- trackerService.removeTrackedResources(intent.getId(),
- installable.requiredLinks());
- }
- }
- processStoreEvent(event);
+ executor.execute(new IntentTask(WITHDRAWING, intent));
}
// FIXME: implement this method
@@ -207,56 +208,122 @@
}
/**
- * Compiles an intent.
+ * Compiles the specified intent.
*
- * @param intent intent
+ * @param intent intent to be compiled
*/
- private void compileIntent(Intent intent) {
- // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
- // TODO: implement compilation traversing tree structure
+ private void executeCompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the compiling phase.
+ store.setState(intent, COMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, associate the resulting list of installable
+ // intents with the top-level intent and proceed to install.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+
+ } catch (Exception e) {
+ // If compilation failed, mark the intent as failed.
+ store.setState(intent, FAILED);
+ }
+ }
+
+ // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
+ // TODO: implement compilation traversing tree structure
+ private List<InstallableIntent> compileIntent(Intent intent) {
List<InstallableIntent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
InstallableIntent installableIntent = (InstallableIntent) compiled;
installable.add(installableIntent);
- trackerService.addTrackedResources(intent.getId(),
+ trackerService.addTrackedResources(intent.id(),
installableIntent.requiredLinks());
}
- IntentEvent event = store.addInstallableIntents(intent.getId(), installable);
- processStoreEvent(event);
+ return installable;
}
/**
- * Installs an intent.
+ * Installs all installable intents associated with the specified top-level
+ * intent.
*
- * @param intent intent
+ * @param intent intent to be installed
*/
- private void installIntent(Intent intent) {
- List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
- if (installables != null) {
- for (InstallableIntent installable : installables) {
- registerSubclassInstallerIfNeeded(installable);
- getInstaller(installable).install(installable);
+ private void executeInstallingPhase(Intent intent) {
+ // Indicate that the intent is entering the installing phase.
+ store.setState(intent, INSTALLING);
+
+ try {
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+ if (installables != null) {
+ for (InstallableIntent installable : installables) {
+ registerSubclassInstallerIfNeeded(installable);
+ getInstaller(installable).install(installable);
+ }
}
- }
- IntentEvent event = store.setState(intent, INSTALLED);
- processStoreEvent(event);
+ eventDispatcher.post(store.setState(intent, INSTALLED));
+ } catch (Exception e) {
+ // If compilation failed, kick off the recompiling phase.
+ executeRecompilingPhase(intent);
+ }
}
/**
- * Uninstalls an intent.
+ * Recompiles the specified intent.
*
- * @param intent intent
+ * @param intent intent to be recompiled
*/
- private void uninstallIntent(Intent intent) {
- List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
+ private void executeRecompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the recompiling phase.
+ store.setState(intent, RECOMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, compare the existing list of installable
+ // intents with the newly compiled list. If they are the same,
+ // bail, out since the previous approach was determined not to
+ // be viable.
+ List<InstallableIntent> originalInstallable =
+ store.getInstallableIntents(intent.id());
+
+ if (Objects.equals(originalInstallable, installable)) {
+ eventDispatcher.post(store.setState(intent, FAILED));
+ } else {
+ // Otherwise, re-associate the newly compiled installable intents
+ // with the top-level intent and kick off installing phase.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+ }
+ } catch (Exception e) {
+ // If compilation failed, mark the intent as failed.
+ eventDispatcher.post(store.setState(intent, FAILED));
+ }
+ }
+
+ /**
+ * Uninstalls the specified intent by uninstalling all of its associated
+ * installable derivatives.
+ *
+ * @param intent intent to be installed
+ */
+ private void executeWithdrawingPhase(Intent intent) {
+ // Indicate that the intent is being withdrawn.
+ store.setState(intent, WITHDRAWING);
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
if (installables != null) {
for (InstallableIntent installable : installables) {
getInstaller(installable).uninstall(installable);
}
}
- store.removeInstalledIntents(intent.getId());
- store.setState(intent, WITHDRAWN);
+
+ // If all went well, disassociate the top-level intent with its
+ // installable derivatives and mark it as withdrawn.
+ store.removeInstalledIntents(intent.id());
+ eventDispatcher.post(store.setState(intent, WITHDRAWN));
}
/**
@@ -309,55 +376,58 @@
}
}
- /**
- * Handles state transition of submitted intents.
- */
- private void processStoreEvent(IntentEvent event) {
- eventDispatcher.post(event);
- Intent intent = event.getIntent();
- try {
- switch (event.getState()) {
- case SUBMITTED:
- compileIntent(intent);
- break;
- case COMPILED:
- installIntent(intent);
- break;
- case INSTALLED:
- break;
- case WITHDRAWING:
- uninstallIntent(intent);
- break;
- case WITHDRAWN:
- break;
- case FAILED:
- break;
- default:
- throw new IllegalStateException("the state of IntentEvent is illegal: " +
- event.getState());
- }
- } catch (IntentException e) {
- store.setState(intent, FAILED);
- }
-
- }
-
// Store delegate to re-post events emitted from the store.
private class InternalStoreDelegate implements IntentStoreDelegate {
@Override
public void notify(IntentEvent event) {
- processStoreEvent(event);
+ eventDispatcher.post(event);
+ if (event.type() == IntentEvent.Type.SUBMITTED) {
+ executor.execute(new IntentTask(COMPILING, event.subject()));
+ }
}
}
// Topology change delegate
private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
@Override
- public void bumpIntents(Iterable<IntentId> intentIds) {
+ public void triggerCompile(Iterable<IntentId> intentIds,
+ boolean compileAllFailed) {
+ // Attempt recompilation of the specified intents first.
for (IntentId intentId : intentIds) {
- compileIntent(getIntent(intentId));
+ executeRecompilingPhase(getIntent(intentId));
+ }
+
+ if (compileAllFailed) {
+ // If required, compile all currently failed intents.
+ for (Intent intent : getIntents()) {
+ if (getIntentState(intent.id()) == FAILED) {
+ executeCompilingPhase(intent);
+ }
+ }
}
}
-
}
+
+ // Auxiliary runnable to perform asynchronous tasks.
+ private class IntentTask implements Runnable {
+ private final IntentState state;
+ private final Intent intent;
+
+ public IntentTask(IntentState state, Intent intent) {
+ this.state = state;
+ this.intent = intent;
+ }
+
+ @Override
+ public void run() {
+ if (state == COMPILING) {
+ executeCompilingPhase(intent);
+ } else if (state == RECOMPILING) {
+ executeRecompilingPhase(intent);
+ } else if (state == WITHDRAWING) {
+ executeWithdrawingPhase(intent);
+ }
+ }
+ }
+
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
similarity index 81%
rename from core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java
rename to core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
index f69bf78..17d420b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
@@ -19,12 +19,15 @@
import org.slf4j.Logger;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
@@ -34,7 +37,7 @@
*/
@Component
@Service
-public class FlowTracker implements FlowTrackerService {
+public class ObjectiveTracker implements ObjectiveTrackerService {
private final Logger log = getLogger(getClass());
@@ -110,14 +113,26 @@
@Override
public void run() {
if (event.reasons() == null) {
- delegate.bumpIntents(intentsByLink.values());
+ delegate.triggerCompile(null, false);
+
} else {
+ Set<IntentId> toBeRecompiled = new HashSet<>();
+ boolean recompileOnly = true;
+
+ // Scan through the list of reasons and keep accruing all
+ // intents that need to be recompiled.
for (Event reason : event.reasons()) {
if (reason instanceof LinkEvent) {
LinkEvent linkEvent = (LinkEvent) reason;
- delegate.bumpIntents(intentsByLink.get(new LinkKey(linkEvent.subject())));
+ if (linkEvent.type() == LINK_REMOVED) {
+ Set<IntentId> intentIds = intentsByLink.get(new LinkKey(linkEvent.subject()));
+ toBeRecompiled.addAll(intentIds);
+ }
+ recompileOnly = recompileOnly && linkEvent.type() == LINK_REMOVED;
}
}
+
+ delegate.triggerCompile(toBeRecompiled, !recompileOnly);
}
}
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
similarity index 96%
rename from core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java
rename to core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
index b96de7c..15496ff 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
@@ -9,7 +9,7 @@
* Auxiliary service for tracking intent path flows and for notifying the
* intent service of environment changes via topology change delegate.
*/
-public interface FlowTrackerService {
+public interface ObjectiveTrackerService {
/**
* Sets a topology change delegate.
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
index f9cfa67..eb2e113 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -49,8 +49,8 @@
@Override
public void install(PathIntent intent) {
TrafficSelector.Builder builder =
- DefaultTrafficSelector.builder(intent.getTrafficSelector());
- Iterator<Link> links = intent.getPath().links().iterator();
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
ConnectPoint prev = links.next().dst();
while (links.hasNext()) {
@@ -70,8 +70,8 @@
@Override
public void uninstall(PathIntent intent) {
TrafficSelector.Builder builder =
- DefaultTrafficSelector.builder(intent.getTrafficSelector());
- Iterator<Link> links = intent.getPath().links().iterator();
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
ConnectPoint prev = links.next().dst();
while (links.hasNext()) {
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
index d8a5a95..7a9fd12 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
@@ -9,10 +9,14 @@
/**
* Notifies that topology has changed in such a way that the specified
- * intents should be recompiled.
+ * intents should be recompiled. If the {@code compileAllFailed} parameter
+ * is true, the all intents in {@link org.onlab.onos.net.intent.IntentState#FAILED}
+ * state should be compiled as well.
*
* @param intentIds intents that should be recompiled
+ * @param compileAllFailed true implies full compile is required; false for
+ * selective recompile only
*/
- void bumpIntents(Iterable<IntentId> intentIds);
+ void triggerCompile(Iterable<IntentId> intentIds, boolean compileAllFailed);
}