From f6ec97b36522fe2ad80e1034bfa8d37634ce7b40 Mon Sep 17 00:00:00 2001 From: Thomas Vachuska Date: Mon, 22 Feb 2016 10:59:23 -0800 Subject: [PATCH] Initial work for adding flow-objective backed intents. Split installation work from IntentManager into IntentInstaller helper class. Change-Id: If926ce975d005abee4f22f2b05404de328d94203 --- .../net/intent/FlowObjectiveIntent.java | 97 +++++++++ .../net/intent/AbstractIntentTest.java | 1 + .../net/intent/FlowObjectiveIntentTest.java | 99 +++++++++ .../net/intent/impl/IntentInstaller.java | 189 ++++++++++++++++++ .../net/intent/impl/IntentManager.java | 138 +------------ 5 files changed, 397 insertions(+), 127 deletions(-) create mode 100644 core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java create mode 100644 core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java create mode 100644 core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java diff --git a/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java b/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java new file mode 100644 index 0000000000..18dcb6e3c1 --- /dev/null +++ b/core/api/src/main/java/org/onosproject/net/intent/FlowObjectiveIntent.java @@ -0,0 +1,97 @@ +/* + * Copyright 2016 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; + +import com.google.common.base.MoreObjects; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.flowobjective.Objective; + +import java.util.Collection; + +/** + * Intent expressed as (and backed by) a collection of flow objectives through + * which the intent is to be accomplished. + */ +public class FlowObjectiveIntent extends Intent { + + private final Collection objectives; + + /** + * Constructor for serialization. + */ + protected FlowObjectiveIntent() { + super(); + this.objectives = null; + } + + /** + * Creates a flow objective intent with the specified objectives and + * resources. + * + * @param appId application id + * @param objectives backing flow objectives + * @param resources backing network resources + */ + public FlowObjectiveIntent(ApplicationId appId, + Collection objectives, + Collection resources) { + this(appId, null, objectives, resources); + } + + /** + * Creates a flow objective intent with the specified objectives and + * resources. + * + * @param appId application id + * @param key intent key + * @param objectives backing flow objectives + * @param resources backing network resources + */ + public FlowObjectiveIntent(ApplicationId appId, Key key, + Collection objectives, + Collection resources) { + super(appId, key, resources, DEFAULT_INTENT_PRIORITY); + this.objectives = objectives; + } + + /** + * Returns the collection of backing flow objectives. + * + * @return flow objectives + */ + Collection objectives() { + return objectives; + } + + + @Override + public boolean isInstallable() { + return true; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id()) + .add("key", key()) + .add("appId", appId()) + .add("resources", resources()) + .add("objectives", objectives) + .toString(); + } +} diff --git a/core/api/src/test/java/org/onosproject/net/intent/AbstractIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/AbstractIntentTest.java index 6bb7e28fea..565bdf5e4c 100644 --- a/core/api/src/test/java/org/onosproject/net/intent/AbstractIntentTest.java +++ b/core/api/src/test/java/org/onosproject/net/intent/AbstractIntentTest.java @@ -25,6 +25,7 @@ public abstract class AbstractIntentTest { @Before public void setUp() throws Exception { + Intent.unbindIdGenerator(idGenerator); Intent.bindIdGenerator(idGenerator); } diff --git a/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java new file mode 100644 index 0000000000..ec3e334e90 --- /dev/null +++ b/core/api/src/test/java/org/onosproject/net/intent/FlowObjectiveIntentTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016 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; + +import com.google.common.collect.ImmutableSet; +import com.google.common.testing.EqualsTester; +import org.junit.Test; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.criteria.Criteria; +import org.onosproject.net.flowobjective.DefaultFilteringObjective; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.flowobjective.Objective; + +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable; + +/** + * Tests of the flow objective intent. + */ +public class FlowObjectiveIntentTest extends IntentTest { + + private static final ApplicationId APP_ID = new DefaultApplicationId(1, "foo"); + private static final Key KEY = Key.of("bar", APP_ID); + + private static final Objective FO1 = DefaultFilteringObjective.builder() + .fromApp(APP_ID).addCondition(Criteria.matchEthType(123)) + .permit().add(); + private static final Objective FO2 = DefaultForwardingObjective.builder() + .fromApp(APP_ID) + .withSelector(DefaultTrafficSelector.builder().matchEthType((short) 123).build()) + .withTreatment(DefaultTrafficTreatment.emptyTreatment()) + .withFlag(ForwardingObjective.Flag.VERSATILE).add(); + private static final Collection OBJECTIVES = ImmutableSet.of(FO1, FO2); + private static final Collection RESOURCES = ImmutableSet.of(); + + /** + * Tests basics of construction and getters. + */ + @Test + public void basics() { + FlowObjectiveIntent intent = + new FlowObjectiveIntent(APP_ID, KEY, OBJECTIVES, RESOURCES); + assertEquals("incorrect app id", APP_ID, intent.appId()); + assertEquals("incorrect key", KEY, intent.key()); + assertEquals("incorrect objectives", OBJECTIVES, intent.objectives()); + assertEquals("incorrect resources", RESOURCES, intent.resources()); + assertTrue("should be installable", intent.isInstallable()); + } + + /** + * Tests equality. + */ + @Test + public void equality() { + Intent a = createOne(); + Intent b = createAnother(); + new EqualsTester().addEqualityGroup(a).addEqualityGroup(b).testEquals(); + } + + /** + * Tests that instance is immutable. + */ + @Test + public void testImmutability() { + assertThatClassIsImmutable(HostToHostIntent.class); + } + + @Override + protected Intent createOne() { + return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES); + } + + @Override + protected Intent createAnother() { + return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES); + } +} \ No newline at end of file diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java new file mode 100644 index 0000000000..772ab7ac1e --- /dev/null +++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java @@ -0,0 +1,189 @@ +/* + * Copyright 2016 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.FlowRule; +import org.onosproject.net.flow.FlowRuleOperations; +import org.onosproject.net.flow.FlowRuleOperationsContext; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.IntentData; +import org.onosproject.net.intent.IntentStore; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.onosproject.net.intent.IntentState.*; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Auxiliary entity responsible for installing the intents into the environment. + */ +class IntentInstaller { + + private static final Logger log = getLogger(IntentManager.class); + + private IntentStore store; + private ObjectiveTrackerService trackerService; + private FlowRuleService flowRuleService; + private FlowObjectiveService flowObjectiveService; + + private enum Direction { + ADD, + REMOVE + } + + /** + * Initializes the installer with references to required services. + * + * @param intentStore intent store + * @param trackerService objective tracking service + * @param flowRuleService flow rule service + * @param flowObjectiveService flow objective service + */ + void init(IntentStore intentStore, ObjectiveTrackerService trackerService, + FlowRuleService flowRuleService, FlowObjectiveService flowObjectiveService) { + this.store = intentStore; + this.trackerService = trackerService; + this.flowRuleService = flowRuleService; + this.flowObjectiveService = flowObjectiveService; + } + + private void applyIntentData(Optional intentData, + FlowRuleOperations.Builder builder, + Direction direction) { + if (!intentData.isPresent()) { + return; + } + IntentData data = intentData.get(); + + List intentsToApply = data.installables(); + if (!intentsToApply.stream().allMatch(x -> x instanceof FlowRuleIntent)) { + throw new IllegalStateException("installable intents must be FlowRuleIntent"); + } + + if (direction == Direction.ADD) { + trackerService.addTrackedResources(data.key(), data.intent().resources()); + intentsToApply.forEach(installable -> + trackerService.addTrackedResources(data.key(), installable.resources())); + } else { + trackerService.removeTrackedResources(data.key(), data.intent().resources()); + intentsToApply.forEach(installable -> + trackerService.removeTrackedResources(data.intent().key(), + installable.resources())); + } + + // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so. + builder.newStage(); + + List> stages = intentsToApply.stream() + .map(x -> (FlowRuleIntent) x) + .map(FlowRuleIntent::flowRules) + .collect(Collectors.toList()); + + for (Collection rules : stages) { + if (direction == Direction.ADD) { + rules.forEach(builder::add); + } else { + rules.forEach(builder::remove); + } + } + + } + + // FIXME: Refactor to accept both FlowObjectiveIntent and FlowRuleIntents + // Note: Intent Manager should have never become dependent on a specific + // intent type. + + /** + * Applies the specified intent updates to the environment by uninstalling + * and installing the intents and updating the store references appropriately. + * + * @param toUninstall optional intent to uninstall + * @param toInstall optional intent to install + */ + void apply(Optional toUninstall, Optional toInstall) { + // need to consider if FlowRuleIntent is only one as installable intent or not + + FlowRuleOperations.Builder builder = FlowRuleOperations.builder(); + applyIntentData(toUninstall, builder, Direction.REMOVE); + applyIntentData(toInstall, builder, Direction.ADD); + + FlowRuleOperations operations = builder.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + if (toInstall.isPresent()) { + IntentData installData = toInstall.get(); + log.debug("Completed installing: {}", installData.key()); + installData.setState(INSTALLED); + store.write(installData); + } else if (toUninstall.isPresent()) { + IntentData uninstallData = toUninstall.get(); + log.debug("Completed withdrawing: {}", uninstallData.key()); + switch (uninstallData.request()) { + case INSTALL_REQ: + uninstallData.setState(FAILED); + break; + case WITHDRAW_REQ: + default: //TODO "default" case should not happen + uninstallData.setState(WITHDRAWN); + break; + } + store.write(uninstallData); + } + } + + @Override + public void onError(FlowRuleOperations ops) { + // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT) + if (toInstall.isPresent()) { + IntentData installData = toInstall.get(); + log.warn("Failed installation: {} {} on {}", + installData.key(), installData.intent(), ops); + installData.setState(CORRUPT); + installData.incrementErrorCount(); + store.write(installData); + } + // if toUninstall was cause of error, then CORRUPT (another job will clean this up) + if (toUninstall.isPresent()) { + IntentData uninstallData = toUninstall.get(); + log.warn("Failed withdrawal: {} {} on {}", + uninstallData.key(), uninstallData.intent(), ops); + uninstallData.setState(CORRUPT); + uninstallData.incrementErrorCount(); + store.write(uninstallData); + } + } + }); + + if (log.isTraceEnabled()) { + log.trace("applying intent {} -> {} with {} rules: {}", + toUninstall.map(x -> x.key().toString()).orElse(""), + toInstall.map(x -> x.key().toString()).orElse(""), + operations.stages().stream().mapToLong(Set::size).sum(), + operations.stages()); + } + + flowRuleService.apply(operations); + } +} 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 c9d6e56a0f..84061f9721 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 @@ -25,11 +25,8 @@ import org.onlab.util.Tools; import org.onosproject.core.CoreService; import org.onosproject.core.IdGenerator; import org.onosproject.event.AbstractListenerManager; -import org.onosproject.net.flow.FlowRule; -import org.onosproject.net.flow.FlowRuleOperations; -import org.onosproject.net.flow.FlowRuleOperationsContext; import org.onosproject.net.flow.FlowRuleService; -import org.onosproject.net.intent.FlowRuleIntent; +import org.onosproject.net.flowobjective.FlowObjectiveService; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentBatchDelegate; import org.onosproject.net.intent.IntentCompiler; @@ -61,13 +58,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.util.concurrent.Executors.newFixedThreadPool; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.onlab.util.Tools.groupedThreads; -import static org.onosproject.net.intent.IntentState.CORRUPT; -import static org.onosproject.net.intent.IntentState.FAILED; -import static org.onosproject.net.intent.IntentState.INSTALLED; -import static org.onosproject.net.intent.IntentState.INSTALL_REQ; -import static org.onosproject.net.intent.IntentState.WITHDRAWING; -import static org.onosproject.net.intent.IntentState.WITHDRAWN; -import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ; +import static org.onosproject.net.intent.IntentState.*; import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure; import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.newInitialPhase; import static org.onosproject.security.AppGuard.checkPermission; @@ -75,7 +66,6 @@ import static org.onosproject.security.AppPermission.Type.INTENT_READ; import static org.onosproject.security.AppPermission.Type.INTENT_WRITE; import static org.slf4j.LoggerFactory.getLogger; - /** * An implementation of intent service. */ @@ -109,12 +99,16 @@ public class IntentManager @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowRuleService flowRuleService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowObjectiveService flowObjectiveService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ResourceService resourceService; private ExecutorService batchExecutor; private ExecutorService workerExecutor; + private final IntentInstaller intentInstaller = new IntentInstaller(); private final CompilerRegistry compilerRegistry = new CompilerRegistry(); private final InternalIntentProcessor processor = new InternalIntentProcessor(); private final IntentStoreDelegate delegate = new InternalStoreDelegate(); @@ -126,6 +120,7 @@ public class IntentManager @Activate public void activate() { + intentInstaller.init(store, trackerService, flowRuleService, flowObjectiveService); store.setDelegate(delegate); trackerService.setDelegate(topoDelegate); eventDispatcher.addSink(IntentEvent.class, listenerRegistry); @@ -138,6 +133,7 @@ public class IntentManager @Deactivate public void deactivate() { + intentInstaller.init(null, null, null, null); store.unsetDelegate(delegate); trackerService.unsetDelegate(topoDelegate); eventDispatcher.removeSink(IntentEvent.class); @@ -322,8 +318,8 @@ public class IntentManager // write multiple data to store in order store.batchWrite(Tools.allOf(futures).join().stream() - .filter(Objects::nonNull) - .collect(Collectors.toList())); + .filter(Objects::nonNull) + .collect(Collectors.toList())); }, batchExecutor).exceptionally(e -> { log.error("Error submitting batches:", e); // FIXME incomplete Intents should be cleaned up @@ -351,120 +347,8 @@ public class IntentManager @Override public void apply(Optional toUninstall, Optional toInstall) { - IntentManager.this.apply(toUninstall, toInstall); + intentInstaller.apply(toUninstall, toInstall); } } - private enum Direction { - ADD, - REMOVE - } - - private void applyIntentData(Optional intentData, - FlowRuleOperations.Builder builder, - Direction direction) { - if (!intentData.isPresent()) { - return; - } - IntentData data = intentData.get(); - - List intentsToApply = data.installables(); - if (!intentsToApply.stream().allMatch(x -> x instanceof FlowRuleIntent)) { - throw new IllegalStateException("installable intents must be FlowRuleIntent"); - } - - if (direction == Direction.ADD) { - trackerService.addTrackedResources(data.key(), data.intent().resources()); - intentsToApply.forEach(installable -> - trackerService.addTrackedResources(data.key(), installable.resources())); - } else { - trackerService.removeTrackedResources(data.key(), data.intent().resources()); - intentsToApply.forEach(installable -> - trackerService.removeTrackedResources(data.intent().key(), - installable.resources())); - } - - // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so. - builder.newStage(); - - List> stages = intentsToApply.stream() - .map(x -> (FlowRuleIntent) x) - .map(FlowRuleIntent::flowRules) - .collect(Collectors.toList()); - - for (Collection rules : stages) { - if (direction == Direction.ADD) { - rules.forEach(builder::add); - } else { - rules.forEach(builder::remove); - } - } - - } - - private void apply(Optional toUninstall, Optional toInstall) { - // need to consider if FlowRuleIntent is only one as installable intent or not - - FlowRuleOperations.Builder builder = FlowRuleOperations.builder(); - applyIntentData(toUninstall, builder, Direction.REMOVE); - applyIntentData(toInstall, builder, Direction.ADD); - - FlowRuleOperations operations = builder.build(new FlowRuleOperationsContext() { - @Override - public void onSuccess(FlowRuleOperations ops) { - if (toInstall.isPresent()) { - IntentData installData = toInstall.get(); - log.debug("Completed installing: {}", installData.key()); - installData.setState(INSTALLED); - store.write(installData); - } else if (toUninstall.isPresent()) { - IntentData uninstallData = toUninstall.get(); - log.debug("Completed withdrawing: {}", uninstallData.key()); - switch (uninstallData.request()) { - case INSTALL_REQ: - uninstallData.setState(FAILED); - break; - case WITHDRAW_REQ: - default: //TODO "default" case should not happen - uninstallData.setState(WITHDRAWN); - break; - } - store.write(uninstallData); - } - } - - @Override - public void onError(FlowRuleOperations ops) { - // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT) - if (toInstall.isPresent()) { - IntentData installData = toInstall.get(); - log.warn("Failed installation: {} {} on {}", - installData.key(), installData.intent(), ops); - installData.setState(CORRUPT); - installData.incrementErrorCount(); - store.write(installData); - } - // if toUninstall was cause of error, then CORRUPT (another job will clean this up) - if (toUninstall.isPresent()) { - IntentData uninstallData = toUninstall.get(); - log.warn("Failed withdrawal: {} {} on {}", - uninstallData.key(), uninstallData.intent(), ops); - uninstallData.setState(CORRUPT); - uninstallData.incrementErrorCount(); - store.write(uninstallData); - } - } - }); - - if (log.isTraceEnabled()) { - log.trace("applying intent {} -> {} with {} rules: {}", - toUninstall.map(x -> x.key().toString()).orElse(""), - toInstall.map(x -> x.key().toString()).orElse(""), - operations.stages().stream().mapToLong(i -> i.size()).sum(), - operations.stages()); - } - - flowRuleService.apply(operations); - } - }