ONOS-1048 - Define interfaces instead of using IntentManager
- Define IntentProcessor interface containing methods to process an intent
- Pull out IntentCompiler related tasks to CompilerRegistry
- Pull out IntentInstaller related tasks to InstallerRegistry
- Create an IntentProcessor subclass as inner class in IntentManager
Change-Id: Ia3e8d574a1053e7ddc9b961873ef758c9e0b1b26
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
index 8fa8b3f..3699df6 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
@@ -16,7 +16,6 @@
package org.onosproject.net.intent.impl;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -27,16 +26,13 @@
import org.onosproject.core.IdGenerator;
import org.onosproject.event.AbstractListenerRegistry;
import org.onosproject.event.EventDeliveryService;
-import org.onosproject.net.flow.FlowRuleOperation;
import org.onosproject.net.flow.FlowRuleOperations;
-import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentBatchDelegate;
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentEvent;
-import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.IntentListener;
@@ -53,29 +49,26 @@
import org.onosproject.net.intent.impl.phase.Withdrawn;
import org.slf4j.Logger;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.EnumSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onlab.util.Tools.isNullOrEmpty;
-import static org.onosproject.net.intent.IntentState.*;
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -95,12 +88,6 @@
private static final EnumSet<IntentState> RECOMPILE
= EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
- // Collections for compiler, installer, and listener are ONOS instance local
- private final ConcurrentMap<Class<? extends Intent>,
- IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
- private final ConcurrentMap<Class<? extends Intent>,
- IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
-
private final AbstractListenerRegistry<IntentEvent, IntentListener>
listenerRegistry = new AbstractListenerRegistry<>();
@@ -124,6 +111,9 @@
private ExecutorService batchExecutor;
private ExecutorService workerExecutor;
+ private final CompilerRegistry compilerRegistry = new CompilerRegistry();
+ private final InstallerRegistry installerRegistry = new InstallerRegistry();
+ private final InternalIntentProcessor processor = new InternalIntentProcessor();
private final IntentStoreDelegate delegate = new InternalStoreDelegate();
private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
@@ -211,259 +201,32 @@
@Override
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
- compilers.put(cls, compiler);
+ compilerRegistry.registerCompiler(cls, compiler);
}
@Override
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
- compilers.remove(cls);
+ compilerRegistry.unregisterCompiler(cls);
}
@Override
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
- return ImmutableMap.copyOf(compilers);
+ return compilerRegistry.getCompilers();
}
@Override
public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
- installers.put(cls, installer);
+ installerRegistry.registerInstaller(cls, installer);
}
@Override
public <T extends Intent> void unregisterInstaller(Class<T> cls) {
- installers.remove(cls);
+ installerRegistry.unregisterInstaller(cls);
}
@Override
public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
- return ImmutableMap.copyOf(installers);
- }
-
- /**
- * Returns the corresponding intent compiler to the specified intent.
- *
- * @param intent intent
- * @param <T> the type of intent
- * @return intent compiler corresponding to the specified intent
- */
- private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
- @SuppressWarnings("unchecked")
- IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
- if (compiler == null) {
- throw new IntentException("no compiler for class " + intent.getClass());
- }
- return compiler;
- }
-
- /**
- * Returns the corresponding intent installer to the specified installable intent.
- *
- * @param intent intent
- * @param <T> the type of installable intent
- * @return intent installer corresponding to the specified installable intent
- */
- private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
- @SuppressWarnings("unchecked")
- IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
- if (installer == null) {
- throw new IntentException("no installer for class " + intent.getClass());
- }
- return installer;
- }
-
- /**
- * Compiles an intent recursively.
- *
- * @param intent intent
- * @param previousInstallables previous intent installables
- * @return result of compilation
- */
- // TODO: make this non-public due to short term hack for ONOS-1051
- public List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
- if (intent.isInstallable()) {
- return ImmutableList.of(intent);
- }
-
- registerSubclassCompilerIfNeeded(intent);
- // FIXME: get previous resources
- List<Intent> installable = new ArrayList<>();
- for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
- installable.addAll(compileIntent(compiled, previousInstallables));
- }
- return installable;
- }
-
- //TODO javadoc
- //FIXME
- // TODO: make this non-public due to short term hack for ONOS-1051
- public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
- List<Intent> oldInstallables = (current != null) ? current.installables() : null;
- List<Intent> newInstallables = pending.installables();
-
- checkState(isNullOrEmpty(oldInstallables) ||
- oldInstallables.size() == newInstallables.size(),
- "Old and New Intent must have equivalent installable intents.");
-
- List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
- for (int i = 0; i < newInstallables.size(); i++) {
- Intent newInstallable = newInstallables.get(i);
- registerSubclassInstallerIfNeeded(newInstallable);
- //TODO consider migrating installers to FlowRuleOperations
- /* FIXME
- - we need to do another pass on this method about that doesn't
- require the length of installables to be equal, and also doesn't
- depend on ordering
- - we should also reconsider when to start/stop tracking resources
- */
- if (isNullOrEmpty(oldInstallables)) {
- plans.add(getInstaller(newInstallable).install(newInstallable));
- } else {
- Intent oldInstallable = oldInstallables.get(i);
- checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
- "Installable Intent type mismatch.");
- trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
- plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
- }
- trackerService.addTrackedResources(pending.key(), newInstallable.resources());
-// } catch (IntentException e) {
-// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
-// //FIXME... we failed. need to uninstall (if same) or revert (if different)
-// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
-// exception = e;
-// batches = uninstallIntent(oldIntent, oldInstallables);
-// }
- }
-
- return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
- @Override
- public void onSuccess(FlowRuleOperations ops) {
- log.debug("Completed installing: {}", pending.key());
- pending.setState(INSTALLED);
- store.write(pending);
- }
-
- @Override
- public void onError(FlowRuleOperations ops) {
- log.warn("Failed installation: {} {} on {}", pending.key(),
- pending.intent(), ops);
- //TODO store.write(pending.setState(BROKEN));
- pending.setState(FAILED);
- store.write(pending);
- }
- });
- }
-
- /**
- * Generate a {@link FlowRuleOperations} instance from the specified intent data.
- *
- * @param current intent data stored in the store
- * @param pending intent data that is pending
- * @return flow rule operations
- */
- // TODO: make this non-public due to short term hack for ONOS-1051
- public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
- List<Intent> installables = current.installables();
- List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
- for (Intent installable : installables) {
- plans.add(getInstaller(installable).uninstall(installable));
- trackerService.removeTrackedResources(pending.key(), installable.resources());
- }
-
- return merge(plans).build(new FlowRuleOperationsContext() {
- @Override
- public void onSuccess(FlowRuleOperations ops) {
- log.debug("Completed withdrawing: {}", pending.key());
- pending.setState(WITHDRAWN);
- pending.setInstallables(Collections.emptyList());
- store.write(pending);
- }
-
- @Override
- public void onError(FlowRuleOperations ops) {
- log.warn("Failed withdraw: {}", pending.key());
- pending.setState(FAILED);
- store.write(pending);
- }
- });
- }
-
-
- // TODO needs tests... or maybe it's just perfect
- private FlowRuleOperations.Builder merge(List<List<Collection<FlowRuleOperation>>> plans) {
- FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
- // Build a batch one stage at a time
- for (int stageNumber = 0;; stageNumber++) {
- // Get the sub-stage from each plan (List<Set<FlowRuleOperation>)
- for (Iterator<List<Collection<FlowRuleOperation>>> itr = plans.iterator(); itr.hasNext();) {
- List<Collection<FlowRuleOperation>> plan = itr.next();
- if (plan.size() <= stageNumber) {
- // we have consumed all stages from this plan, so remove it
- itr.remove();
- continue;
- }
- // write operations from this sub-stage into the builder
- Collection<FlowRuleOperation> stage = plan.get(stageNumber);
- for (FlowRuleOperation entry : stage) {
- builder.operation(entry);
- }
- }
- // we are done with the stage, start the next one...
- if (plans.isEmpty()) {
- break; // we don't need to start a new stage, we are done.
- }
- builder.newStage();
- }
- return builder;
- }
-
- /**
- * Registers an intent compiler of the specified intent if an intent compiler
- * for the intent is not registered. This method traverses the class hierarchy of
- * the intent. Once an intent compiler for a parent type is found, this method
- * registers the found intent compiler.
- *
- * @param intent intent
- */
- private void registerSubclassCompilerIfNeeded(Intent intent) {
- if (!compilers.containsKey(intent.getClass())) {
- Class<?> cls = intent.getClass();
- while (cls != Object.class) {
- // As long as we're within the Intent class descendants
- if (Intent.class.isAssignableFrom(cls)) {
- IntentCompiler<?> compiler = compilers.get(cls);
- if (compiler != null) {
- compilers.put(intent.getClass(), compiler);
- return;
- }
- }
- cls = cls.getSuperclass();
- }
- }
- }
-
- /**
- * Registers an intent installer of the specified intent if an intent installer
- * for the intent is not registered. This method traverses the class hierarchy of
- * the intent. Once an intent installer for a parent type is found, this method
- * registers the found intent installer.
- *
- * @param intent intent
- */
- private void registerSubclassInstallerIfNeeded(Intent intent) {
- if (!installers.containsKey(intent.getClass())) {
- Class<?> cls = intent.getClass();
- while (cls != Object.class) {
- // As long as we're within the Intent class descendants
- if (Intent.class.isAssignableFrom(cls)) {
- IntentInstaller<?> installer = installers.get(cls);
- if (installer != null) {
- installers.put(intent.getClass(), installer);
- return;
- }
- }
- cls = cls.getSuperclass();
- }
- }
+ return installerRegistry.getInstallers();
}
// Store delegate to re-post events emitted from the store.
@@ -525,12 +288,12 @@
IntentData current = store.getIntentData(intentData.key());
switch (intentData.state()) {
case INSTALL_REQ:
- return new InstallRequest(this, intentData, Optional.ofNullable(current));
+ return new InstallRequest(processor, intentData, Optional.ofNullable(current));
case WITHDRAW_REQ:
if (current == null || isNullOrEmpty(current.installables())) {
return new Withdrawn(intentData, WITHDRAWN);
} else {
- return new WithdrawRequest(this, intentData, current);
+ return new WithdrawRequest(processor, intentData, current);
}
default:
// illegal state
@@ -648,4 +411,26 @@
// TODO ensure that only one batch is in flight at a time
}
}
+
+ private class InternalIntentProcessor implements IntentProcessor {
+ @Override
+ public List<Intent> compile(Intent intent, List<Intent> previousInstallables) {
+ return compilerRegistry.compile(intent, previousInstallables);
+ }
+
+ @Override
+ public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
+ return installerRegistry.coordinate(current, pending, store, trackerService);
+ }
+
+ @Override
+ public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
+ return installerRegistry.uninstallCoordinate(current, pending, store, trackerService);
+ }
+
+ @Override
+ public void applyFlowRules(FlowRuleOperations flowRules) {
+ flowRuleService.apply(flowRules);
+ }
+ }
}