Sho SHIMIZU

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
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
// TODO: consider a better name
class CompilerRegistry {
private final ConcurrentMap<Class<? extends Intent>,
IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
/**
* Registers the specified compiler for the given intent class.
*
* @param cls intent class
* @param compiler intent compiler
* @param <T> the type of intent
*/
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
/**
* Unregisters the compiler for the specified intent class.
*
* @param cls intent class
* @param <T> the type of intent
*/
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
compilers.remove(cls);
}
/**
* Returns immutable set of bindings of currently registered intent compilers.
*
* @return the set of compiler bindings
*/
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
return ImmutableMap.copyOf(compilers);
}
/**
* Compiles an intent recursively.
*
* @param intent intent
* @param previousInstallables previous intent installables
* @return result of compilation
*/
List<Intent> compile(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(compile(compiled, previousInstallables));
}
return installable;
}
/**
* 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;
}
/**
* 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();
}
}
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl;
import com.google.common.collect.ImmutableMap;
import org.onosproject.net.flow.FlowRuleOperation;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.IntentStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.google.common.base.Preconditions.checkState;
import static org.onlab.util.Tools.isNullOrEmpty;
import static org.onosproject.net.intent.IntentState.FAILED;
import static org.onosproject.net.intent.IntentState.INSTALLED;
import static org.onosproject.net.intent.IntentState.WITHDRAWN;
// TODO: consider a better name
class InstallerRegistry {
private static final Logger log = LoggerFactory.getLogger(InstallerRegistry.class);
private final ConcurrentMap<Class<? extends Intent>,
IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
/**
* Registers the specified installer for the given installable intent class.
*
* @param cls installable intent class
* @param installer intent installer
* @param <T> the type of installable intent
*/
<T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
installers.put(cls, installer);
}
/**
* Unregisters the installer for the given installable intent class.
*
* @param cls installable intent class
* @param <T> the type of installable intent
*/
<T extends Intent> void unregisterInstaller(Class<T> cls) {
installers.remove(cls);
}
/**
* Returns immutable set of bindings of currently registered intent installers.
*
* @return the set of installer bindings
*/
Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
return ImmutableMap.copyOf(installers);
}
/**
* 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;
}
/**
* 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();
}
}
}
/**
* Generate a {@link FlowRuleOperations} instance from the specified intent data.
*
* @param current intent data stored in the store
* @param pending intent data being processed
* @param store intent store saving the intent state in this method
* @param trackerService objective tracker that is used in this method
* @return flow rule operations
*/
public FlowRuleOperations coordinate(IntentData current, IntentData pending,
IntentStore store, ObjectiveTrackerService trackerService) {
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 date being processed
* @param store intent store saving the intent state in this method
* @param trackerService objective tracker that is used in this method
* @return flow rule operations
*/
FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending,
IntentStore store, ObjectiveTrackerService trackerService) {
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;
}
}
......@@ -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.CoreService;
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.WithdrawRequest;
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 @@ public class IntentManager
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 @@ public class IntentManager
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 @@ public class IntentManager
@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 @@ public class IntentManager
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 @@ public class IntentManager
// 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);
}
}
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import java.util.List;
/**
* A collection of methods to process an intent.
*
* This interface is public, but intended to be used only by IntentManager and
* IntentProcessPhase subclasses stored under phase package.
*/
public interface IntentProcessor {
/**
* Compiles an intent recursively.
*
* @param intent intent
* @param previousInstallables previous intent installables
* @return result of compilation
*/
List<Intent> compile(Intent intent, List<Intent> previousInstallables);
/**
* Generate a {@link FlowRuleOperations} instance from the specified intent data.
*
* @param current intent data stored in the store
* @param pending intent data being processed
* @return flow rule operations
*/
FlowRuleOperations coordinate(IntentData current, IntentData pending);
/**
* Generate a {@link FlowRuleOperations} instance from the specified intent data.
*
* @param current intent data stored in the store
* @param pending intent data being processed
* @return flow rule operations
*/
FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending);
/**
* Applies a batch operation of FlowRules.
*
* @param flowRules batch operation to apply
*/
// TODO: consider a better name
// This methods gives strangeness a bit because
// it doesn't receive/return intent related information
void applyFlowRules(FlowRuleOperations flowRules);
}
......@@ -18,7 +18,7 @@ package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -34,13 +34,12 @@ final class Compiling implements IntentProcessPhase {
private static final Logger log = LoggerFactory.getLogger(Compiling.class);
// TODO: define an interface and use it, instead of IntentManager
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final IntentData current;
Compiling(IntentManager intentManager, IntentData pending, IntentData current) {
this.intentManager = checkNotNull(intentManager);
Compiling(IntentProcessor processor, IntentData pending, IntentData current) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(pending);
this.current = current;
}
......@@ -49,11 +48,12 @@ final class Compiling implements IntentProcessPhase {
public Optional<IntentProcessPhase> execute() {
try {
List<Intent> installables = (current != null) ? current.installables() : null;
pending.setInstallables(intentManager.compileIntent(pending.intent(), installables));
return Optional.of(new InstallCoordinating(intentManager, pending, current));
pending.setInstallables(processor.compile(pending.intent(), installables));
return Optional.of(new InstallCoordinating(processor, pending, current));
} catch (IntentException e) {
log.debug("Unable to compile intent {} due to: {}", pending.intent(), e);
return Optional.of(new CompilingFailed(pending));
}
}
}
......
......@@ -18,7 +18,7 @@ package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -34,13 +34,13 @@ final class InstallCoordinating implements IntentProcessPhase {
private static final Logger log = LoggerFactory.getLogger(InstallCoordinating.class);
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final IntentData current;
// TODO: define an interface and use it, instead of IntentManager
InstallCoordinating(IntentManager intentManager, IntentData pending, IntentData current) {
this.intentManager = checkNotNull(intentManager);
InstallCoordinating(IntentProcessor processor, IntentData pending, IntentData current) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(pending);
this.current = current;
}
......@@ -50,8 +50,8 @@ final class InstallCoordinating implements IntentProcessPhase {
try {
//FIXME we orphan flow rules that are currently on the data plane
// ... should either reuse them or remove them
FlowRuleOperations flowRules = intentManager.coordinate(current, pending);
return Optional.of(new Installing(intentManager, pending, flowRules));
FlowRuleOperations flowRules = processor.coordinate(current, pending);
return Optional.of(new Installing(processor, pending, flowRules));
} catch (IntentException e) {
log.warn("Unable to generate a FlowRuleOperations from intent {} due to:", pending.intent().id(), e);
return Optional.of(new InstallingFailed(pending));
......
......@@ -16,7 +16,7 @@
package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import java.util.Optional;
......@@ -27,13 +27,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class InstallRequest implements IntentProcessPhase {
// TODO: define an interface and use it, instead of IntentManager
private final IntentManager intentManager;
private final IntentProcessor intentManager;
private final IntentData pending;
private final Optional<IntentData> current;
public InstallRequest(IntentManager intentManager, IntentData intentData, Optional<IntentData> current) {
this.intentManager = checkNotNull(intentManager);
public InstallRequest(IntentProcessor processor, IntentData intentData, Optional<IntentData> current) {
this.intentManager = checkNotNull(processor);
this.pending = checkNotNull(intentData);
this.current = checkNotNull(current);
}
......
......@@ -18,7 +18,7 @@ package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -34,13 +34,12 @@ final class Installing implements IntentProcessPhase {
private static final Logger log = LoggerFactory.getLogger(Installing.class);
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final FlowRuleOperations flowRules;
// TODO: define an interface and use it, instead of IntentManager
Installing(IntentManager intentManager, IntentData pending, FlowRuleOperations flowRules) {
this.intentManager = checkNotNull(intentManager);
Installing(IntentProcessor processor, IntentData pending, FlowRuleOperations flowRules) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(pending);
this.flowRules = flowRules;
}
......@@ -48,7 +47,7 @@ final class Installing implements IntentProcessPhase {
@Override
public Optional<IntentProcessPhase> execute() {
try {
intentManager.flowRuleService.apply(flowRules); // FIXME we need to provide a context
processor.applyFlowRules(flowRules);
return Optional.of(new Installed(pending));
// What kinds of exceptions are thrown by FlowRuleService.apply()?
// Is IntentException a correct exception abstraction?
......
......@@ -18,7 +18,7 @@ package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -34,13 +34,12 @@ final class WithdrawCoordinating implements IntentProcessPhase {
private static final Logger log = LoggerFactory.getLogger(WithdrawCoordinating.class);
// TODO: define an interface and use it, instead of IntentManager
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final IntentData current;
WithdrawCoordinating(IntentManager intentManager, IntentData pending, IntentData current) {
this.intentManager = checkNotNull(intentManager);
WithdrawCoordinating(IntentProcessor processor, IntentData pending, IntentData current) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(pending);
this.current = checkNotNull(current);
}
......@@ -49,9 +48,9 @@ final class WithdrawCoordinating implements IntentProcessPhase {
public Optional<IntentProcessPhase> execute() {
try {
// Note: current.installables() are not null or empty due to createIntentUpdate check
FlowRuleOperations flowRules = intentManager.uninstallCoordinate(current, pending);
FlowRuleOperations flowRules = processor.uninstallCoordinate(current, pending);
pending.setInstallables(current.installables());
return Optional.of(new Withdrawing(intentManager, pending, flowRules));
return Optional.of(new Withdrawing(processor, pending, flowRules));
} catch (IntentException e) {
log.warn("Unable to generate generate a FlowRuleOperations from intent {} due to:", pending.intent(), e);
return Optional.of(new WithdrawingFailed(pending));
......
......@@ -16,7 +16,7 @@
package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import java.util.Optional;
......@@ -27,13 +27,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class WithdrawRequest implements IntentProcessPhase {
// TODO: define an interface and use it, instead of IntentManager
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final IntentData current;
public WithdrawRequest(IntentManager intentManager, IntentData intentData, IntentData current) {
this.intentManager = checkNotNull(intentManager);
public WithdrawRequest(IntentProcessor processor, IntentData intentData, IntentData current) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(intentData);
this.current = checkNotNull(current);
}
......@@ -43,6 +42,6 @@ public final class WithdrawRequest implements IntentProcessPhase {
//TODO perhaps we want to validate that the pending and current are the
// same version i.e. they are the same
// Note: this call is not just the symmetric version of submit
return Optional.of(new WithdrawCoordinating(intentManager, pending, current));
return Optional.of(new WithdrawCoordinating(processor, pending, current));
}
}
......
......@@ -17,7 +17,7 @@ package org.onosproject.net.intent.impl.phase;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.impl.IntentProcessor;
import java.util.Optional;
......@@ -29,20 +29,19 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
class Withdrawing implements IntentProcessPhase {
// TODO: define an interface and use it, instead of IntentManager
private final IntentManager intentManager;
private final IntentProcessor processor;
private final IntentData pending;
private final FlowRuleOperations flowRules;
Withdrawing(IntentManager intentManager, IntentData pending, FlowRuleOperations flowRules) {
this.intentManager = checkNotNull(intentManager);
Withdrawing(IntentProcessor processor, IntentData pending, FlowRuleOperations flowRules) {
this.processor = checkNotNull(processor);
this.pending = checkNotNull(pending);
this.flowRules = checkNotNull(flowRules);
}
@Override
public Optional<IntentProcessPhase> execute() {
intentManager.flowRuleService.apply(flowRules);
processor.applyFlowRules(flowRules);
return Optional.of(new Withdrawn(pending));
}
}
......