Refactored intent framework to deal with batches.

There is still work to be done, but for now, submit, withdraw and reroute are working.

Change-Id: Ib94cf8c4be03786cc070f402d1f296f5dfa6588b
This commit is contained in:
Brian O'Connor 2014-10-30 13:20:05 -07:00 committed by Gerrit Code Review
parent a88d1f5943
commit fa81eaef06
27 changed files with 829 additions and 252 deletions

View File

@ -29,6 +29,7 @@ import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentEvent; import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentEvent.Type; import org.onlab.onos.net.intent.IntentEvent.Type;
import org.onlab.onos.net.intent.IntentListener; import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentOperations;
import org.onlab.onos.net.intent.IntentService; import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.PointToPointIntent; import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.packet.Ethernet; import org.onlab.packet.Ethernet;
@ -63,9 +64,6 @@ public class IntentPushTestCommand extends AbstractShellCommand
required = true, multiValued = false) required = true, multiValued = false)
String countString = null; String countString = null;
private static long id = 0x7870001;
private IntentService service; private IntentService service;
private CountDownLatch latch; private CountDownLatch latch;
private long start, end; private long start, end;
@ -91,15 +89,18 @@ public class IntentPushTestCommand extends AbstractShellCommand
service.addListener(this); service.addListener(this);
latch = new CountDownLatch(count); latch = new CountDownLatch(count);
start = System.currentTimeMillis(); IntentOperations.Builder ops = IntentOperations.builder();
for (int i = 0; i < count; i++) { for (int i = 1; i <= count; i++) {
TrafficSelector s = selector TrafficSelector s = selector
.matchEthSrc(MacAddress.valueOf(i)) .matchEthSrc(MacAddress.valueOf(i))
.build(); .build();
Intent intent = new PointToPointIntent(appId(), s, treatment, Intent intent = new PointToPointIntent(appId(), s, treatment,
ingress, egress); ingress, egress);
service.submit(intent); ops.addSubmitOperation(intent);
} }
IntentOperations operations = ops.build();
start = System.currentTimeMillis();
service.execute(operations);
try { try {
if (latch.await(10, TimeUnit.SECONDS)) { if (latch.await(10, TimeUnit.SECONDS)) {
printResults(count); printResults(count);

View File

@ -15,13 +15,13 @@
*/ */
package org.onlab.onos.net.flow; package org.onlab.onos.net.flow;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* A list of BatchOperationEntry. * A list of BatchOperationEntry.
* *
@ -88,6 +88,16 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
return ops.add(entry) ? this : null; return ops.add(entry) ? this : null;
} }
/**
* Add all operations from another batch to this batch.
*
* @param another another batch
* @return true if success
*/
public boolean addAll(BatchOperation<T> another) {
return ops.addAll(another.getOperations());
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {

View File

@ -19,12 +19,20 @@ import java.util.Set;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/**
* Representation of a completed flow rule batch operation.
*/
public class CompletedBatchOperation implements BatchOperationResult<FlowRule> { public class CompletedBatchOperation implements BatchOperationResult<FlowRule> {
private final boolean success; private final boolean success;
private final Set<FlowRule> failures; private final Set<FlowRule> failures;
/**
* Creates a new batch completion result.
*
* @param success indicates whether the completion is successful.
* @param failures set of any failures encountered
*/
public CompletedBatchOperation(boolean success, Set<? extends FlowRule> failures) { public CompletedBatchOperation(boolean success, Set<? extends FlowRule> failures) {
this.success = success; this.success = success;
this.failures = ImmutableSet.copyOf(failures); this.failures = ImmutableSet.copyOf(failures);
@ -40,5 +48,4 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> {
return failures; return failures;
} }
} }

View File

@ -0,0 +1,36 @@
/*
* Copyright 2014 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.onlab.onos.net.intent;
/**
* Facade for receiving notifications from the intent batch service.
*/
public interface IntentBatchDelegate {
/**
* Submits the specified batch of intent operations for processing.
*
* @param operations batch of operations
*/
void execute(IntentOperations operations);
/**
* Cancesl the specified batch of intent operations.
*
* @param operations batch of operations to be cancelled
*/
void cancel(IntentOperations operations);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2014 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.onlab.onos.net.intent;
import java.util.Set;
/**
* Service for tracking and delegating batches of intent operations.
*/
public interface IntentBatchService {
/**
* Submits a batch of intent operations.
*
* @param operations batch of operations
*/
void addIntentOperations(IntentOperations operations);
/**
* Removes the specified batch of intent operations after completion.
*
* @param operations batch of operations
*/
void removeIntentOperations(IntentOperations operations);
/**
* Returns the set of intent batches currently being tracked.
* @return set of batches
*/
Set<IntentOperations> getIntentOperations();
/**
* Sets the batch service delegate.
*
* @param delegate delegate to apply
*/
void setDelegate(IntentBatchDelegate delegate);
/**
* Unsets the batch service delegate.
*
* @param delegate delegate to unset
*/
void unsetDelegate(IntentBatchDelegate delegate);
}

View File

@ -15,7 +15,10 @@
*/ */
package org.onlab.onos.net.intent; package org.onlab.onos.net.intent;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* Abstraction of a compiler which is capable of taking an intent * Abstraction of a compiler which is capable of taking an intent
@ -27,9 +30,13 @@ public interface IntentCompiler<T extends Intent> {
/** /**
* Compiles the specified intent into other intents. * Compiles the specified intent into other intents.
* *
* @param intent intent to be compiled * @param intent intent to be compiled
* @param installable previously compilation result; optional
* @param resources previously allocated resources; optional
* @return list of resulting intents * @return list of resulting intents
* @throws IntentException if issues are encountered while compiling the intent * @throws IntentException if issues are encountered while compiling the intent
*/ */
List<Intent> compile(T intent); List<Intent> compile(T intent, List<Intent> installable,
Set<LinkResourceAllocations> resources);
} }

View File

@ -15,10 +15,10 @@
*/ */
package org.onlab.onos.net.intent; package org.onlab.onos.net.intent;
import java.util.List;
import org.onlab.onos.net.flow.FlowRuleBatchOperation; import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import java.util.List;
/** /**
* Abstraction of entity capable of installing intents to the environment. * Abstraction of entity capable of installing intents to the environment.
*/ */
@ -26,8 +26,8 @@ public interface IntentInstaller<T extends Intent> {
/** /**
* Installs the specified intent to the environment. * Installs the specified intent to the environment.
* *
* @param intent intent to be installed * @param intent intent to be installed
* @return FlowRule operations to install * @return flow rule operations to complete install
* @throws IntentException if issues are encountered while installing the intent * @throws IntentException if issues are encountered while installing the intent
*/ */
List<FlowRuleBatchOperation> install(T intent); List<FlowRuleBatchOperation> install(T intent);
@ -35,9 +35,20 @@ public interface IntentInstaller<T extends Intent> {
/** /**
* Uninstalls the specified intent from the environment. * Uninstalls the specified intent from the environment.
* *
* @param intent intent to be uninstalled * @param intent intent to be uninstalled
* @return FlowRule operations to uninstall * @return flow rule operations to complete uninstall
* @throws IntentException if issues are encountered while uninstalling the intent * @throws IntentException if issues are encountered while uninstalling the intent
*/ */
List<FlowRuleBatchOperation> uninstall(T intent); List<FlowRuleBatchOperation> uninstall(T intent);
/**
* Replaces the specified intent with a new one in the environment.
*
* @param oldIntent intent to be removed
* @param newIntent intent to be installed
* @return flow rule operations to complete the replace
* @throws IntentException if issues are encountered while uninstalling the intent
*/
List<FlowRuleBatchOperation> replace(T oldIntent, T newIntent);
} }

View File

@ -15,6 +15,11 @@
*/ */
package org.onlab.onos.net.intent; package org.onlab.onos.net.intent;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/** /**
* Abstraction of an intent-related operation, e.g. add, remove, replace. * Abstraction of an intent-related operation, e.g. add, remove, replace.
*/ */
@ -27,7 +32,7 @@ public class IntentOperation {
/** /**
* Operation type. * Operation type.
*/ */
enum Type { public enum Type {
/** /**
* Indicates that an intent should be added. * Indicates that an intent should be added.
*/ */
@ -41,15 +46,20 @@ public class IntentOperation {
/** /**
* Indicates that an intent should be replaced with another. * Indicates that an intent should be replaced with another.
*/ */
REPLACE REPLACE,
/**
* Indicates that an intent should be updated (i.e. recompiled/reinstalled).
*/
UPDATE,
} }
/** /**
* Creates an intent operation. * Creates an intent operation.
* *
* @param type operation type * @param type operation type
* @param intentId identifier of the intent subject to the operation * @param intentId identifier of the intent subject to the operation
* @param intent intent subject * @param intent intent subject
*/ */
IntentOperation(Type type, IntentId intentId, Intent intent) { IntentOperation(Type type, IntentId intentId, Intent intent) {
this.type = type; this.type = type;
@ -85,4 +95,32 @@ public class IntentOperation {
return intent; return intent;
} }
@Override
public int hashCode() {
return Objects.hash(type, intentId, intent);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final IntentOperation other = (IntentOperation) obj;
return Objects.equals(this.type, other.type) &&
Objects.equals(this.intentId, other.intentId) &&
Objects.equals(this.intent, other.intent);
}
@Override
public String toString() {
return toStringHelper(this)
.add("type", type)
.add("intentId", intentId)
.add("intent", intent)
.toString();
}
} }

View File

@ -18,11 +18,11 @@ package org.onlab.onos.net.intent;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.intent.IntentOperation.Type.REPLACE; import static org.onlab.onos.net.intent.IntentOperation.Type.*;
import static org.onlab.onos.net.intent.IntentOperation.Type.SUBMIT;
import static org.onlab.onos.net.intent.IntentOperation.Type.WITHDRAW;
/** /**
* Batch of intent submit/withdraw/replace operations. * Batch of intent submit/withdraw/replace operations.
@ -58,6 +58,31 @@ public final class IntentOperations {
return new Builder(); return new Builder();
} }
@Override
public int hashCode() {
return Objects.hash(operations);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final IntentOperations other = (IntentOperations) obj;
return Objects.equals(this.operations, other.operations);
}
@Override
public String toString() {
return toStringHelper(this)
.add("operations", operations)
.toString();
}
/** /**
* Builder for batches of intent operations. * Builder for batches of intent operations.
*/ */
@ -107,6 +132,18 @@ public final class IntentOperations {
return this; return this;
} }
/**
* Adds an intent update operation.
*
* @param intentId identifier of the intent to be updated
* @return self
*/
public Builder addUpdateOperation(IntentId intentId) {
checkNotNull(intentId, "Intent ID cannot be null");
builder.add(new IntentOperation(UPDATE, intentId, null));
return this;
}
/** /**
* Builds a batch of intent operations. * Builds a batch of intent operations.
* *

View File

@ -17,7 +17,6 @@ package org.onlab.onos.net.intent;
import java.util.List; import java.util.List;
import java.util.concurrent.Future;
/** /**
* Service for application submitting or withdrawing their intents. * Service for application submitting or withdrawing their intents.
@ -59,9 +58,8 @@ public interface IntentService {
* affected at later time. * affected at later time.
* </p> * </p>
* @param operations batch of intent operations * @param operations batch of intent operations
* @return Future to get execution result
*/ */
Future<IntentOperations> execute(IntentOperations operations); void execute(IntentOperations operations);
/** /**
* Returns an iterable of intents currently in the system. * Returns an iterable of intents currently in the system.

View File

@ -24,7 +24,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/** /**
* Fake implementation of the intent service to assist in developing tests of * Fake implementation of the intent service to assist in developing tests of
@ -104,7 +103,7 @@ public class FakeIntentManager implements TestableIntentService {
try { try {
// For the fake, we compile using a single level pass // For the fake, we compile using a single level pass
List<Intent> installable = new ArrayList<>(); List<Intent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) { for (Intent compiled : getCompiler(intent).compile(intent, null, null)) {
installable.add((Intent) compiled); installable.add((Intent) compiled);
} }
executeInstallingPhase(intent, installable); executeInstallingPhase(intent, installable);
@ -192,9 +191,8 @@ public class FakeIntentManager implements TestableIntentService {
} }
@Override @Override
public Future<IntentOperations> execute(IntentOperations operations) { public void execute(IntentOperations operations) {
// TODO: implement later // TODO: implement later
return null;
} }
@Override @Override

View File

@ -29,11 +29,13 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.onlab.onos.net.flow.FlowRuleBatchOperation; import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.resource.LinkResourceAllocations;
/** /**
* Suite of tests for the intent service contract. * Suite of tests for the intent service contract.
@ -294,7 +296,8 @@ public class IntentServiceTest {
} }
@Override @Override
public List<Intent> compile(TestIntent intent) { public List<Intent> compile(TestIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
if (fail) { if (fail) {
throw new IntentException("compile failed by design"); throw new IntentException("compile failed by design");
} }
@ -326,6 +329,12 @@ public class IntentServiceTest {
} }
return null; return null;
} }
@Override
public List<FlowRuleBatchOperation> replace(TestInstallableIntent intent,
TestInstallableIntent newIntent) {
return null;
}
} }
} }

View File

@ -509,13 +509,8 @@ public class FlowRuleManager
boolean success = true; boolean success = true;
Set<FlowRule> failed = Sets.newHashSet(); Set<FlowRule> failed = Sets.newHashSet();
CompletedBatchOperation completed; CompletedBatchOperation completed;
long start = System.nanoTime();
long end = start + unit.toNanos(timeout);
for (Future<CompletedBatchOperation> future : futures) { for (Future<CompletedBatchOperation> future : futures) {
long now = System.nanoTime(); completed = future.get(timeout, unit);
long thisTimeout = end - now;
completed = future.get(thisTimeout, TimeUnit.NANOSECONDS);
success = validateBatchOperation(failed, completed); success = validateBatchOperation(failed, completed);
} }
return finalizeBatchOperation(success, failed); return finalizeBatchOperation(success, failed);

View File

@ -27,9 +27,11 @@ import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.HostToHostIntent; import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.Intent; import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.PathIntent; import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder; import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
@ -54,7 +56,8 @@ public class HostToHostIntentCompiler
} }
@Override @Override
public List<Intent> compile(HostToHostIntent intent) { public List<Intent> compile(HostToHostIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
Path pathOne = getPath(intent, intent.one(), intent.two()); Path pathOne = getPath(intent, intent.one(), intent.two());
Path pathTwo = getPath(intent, intent.two(), intent.one()); Path pathTwo = getPath(intent, intent.two(), intent.one());

View File

@ -15,31 +15,10 @@
*/ */
package org.onlab.onos.net.intent.impl; package org.onlab.onos.net.intent.impl;
import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList;
import static java.util.concurrent.Executors.newSingleThreadExecutor; import com.google.common.collect.ImmutableMap;
import static org.onlab.onos.net.intent.IntentState.COMPILING; import com.google.common.collect.Lists;
import static org.onlab.onos.net.intent.IntentState.FAILED; import com.google.common.collect.Maps;
import static org.onlab.onos.net.intent.IntentState.INSTALLED;
import static org.onlab.onos.net.intent.IntentState.INSTALLING;
import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.ArrayList;
import java.util.Iterator;
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.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Deactivate;
@ -52,6 +31,8 @@ import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.flow.FlowRuleBatchOperation; import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.FlowRuleService; import org.onlab.onos.net.flow.FlowRuleService;
import org.onlab.onos.net.intent.Intent; import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentBatchDelegate;
import org.onlab.onos.net.intent.IntentBatchService;
import org.onlab.onos.net.intent.IntentCompiler; import org.onlab.onos.net.intent.IntentCompiler;
import org.onlab.onos.net.intent.IntentEvent; import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentException; import org.onlab.onos.net.intent.IntentException;
@ -59,6 +40,7 @@ import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentId; import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentInstaller; import org.onlab.onos.net.intent.IntentInstaller;
import org.onlab.onos.net.intent.IntentListener; import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentOperation;
import org.onlab.onos.net.intent.IntentOperations; import org.onlab.onos.net.intent.IntentOperations;
import org.onlab.onos.net.intent.IntentService; import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.IntentState; import org.onlab.onos.net.intent.IntentState;
@ -66,9 +48,24 @@ import org.onlab.onos.net.intent.IntentStore;
import org.onlab.onos.net.intent.IntentStoreDelegate; import org.onlab.onos.net.intent.IntentStoreDelegate;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.google.common.collect.ImmutableList; import java.util.ArrayList;
import com.google.common.collect.ImmutableMap; import java.util.List;
import com.google.common.collect.Lists; import java.util.Map;
import java.util.Objects;
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.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.google.common.base.Preconditions.checkArgument;
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;
/** /**
* An implementation of Intent Manager. * An implementation of Intent Manager.
@ -96,10 +93,14 @@ public class IntentManager
private final IntentStoreDelegate delegate = new InternalStoreDelegate(); private final IntentStoreDelegate delegate = new InternalStoreDelegate();
private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate(); private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentStore store; protected IntentStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentBatchService batchService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ObjectiveTrackerService trackerService; protected ObjectiveTrackerService trackerService;
@ -113,6 +114,7 @@ public class IntentManager
public void activate() { public void activate() {
store.setDelegate(delegate); store.setDelegate(delegate);
trackerService.setDelegate(topoDelegate); trackerService.setDelegate(topoDelegate);
batchService.setDelegate(batchDelegate);
eventDispatcher.addSink(IntentEvent.class, listenerRegistry); eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
executor = newSingleThreadExecutor(namedThreads("onos-intents")); executor = newSingleThreadExecutor(namedThreads("onos-intents"));
monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor")); monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
@ -123,6 +125,7 @@ public class IntentManager
public void deactivate() { public void deactivate() {
store.unsetDelegate(delegate); store.unsetDelegate(delegate);
trackerService.unsetDelegate(topoDelegate); trackerService.unsetDelegate(topoDelegate);
batchService.unsetDelegate(batchDelegate);
eventDispatcher.removeSink(IntentEvent.class); eventDispatcher.removeSink(IntentEvent.class);
executor.shutdown(); executor.shutdown();
monitorExecutor.shutdown(); monitorExecutor.shutdown();
@ -132,30 +135,27 @@ public class IntentManager
@Override @Override
public void submit(Intent intent) { public void submit(Intent intent) {
checkNotNull(intent, INTENT_NULL); checkNotNull(intent, INTENT_NULL);
registerSubclassCompilerIfNeeded(intent); execute(IntentOperations.builder().addSubmitOperation(intent).build());
IntentEvent event = store.createIntent(intent);
if (event != null) {
eventDispatcher.post(event);
executor.execute(new IntentTask(COMPILING, intent));
}
} }
@Override @Override
public void withdraw(Intent intent) { public void withdraw(Intent intent) {
checkNotNull(intent, INTENT_NULL); checkNotNull(intent, INTENT_NULL);
executor.execute(new IntentTask(WITHDRAWING, intent)); execute(IntentOperations.builder().addWithdrawOperation(intent.id()).build());
} }
// FIXME: implement this method
@Override @Override
public void replace(IntentId oldIntentId, Intent newIntent) { public void replace(IntentId oldIntentId, Intent newIntent) {
throw new UnsupportedOperationException("execute() is not implemented yet"); checkNotNull(oldIntentId, INTENT_ID_NULL);
checkNotNull(newIntent, INTENT_NULL);
execute(IntentOperations.builder()
.addReplaceOperation(oldIntentId, newIntent)
.build());
} }
// FIXME: implement this method
@Override @Override
public Future<IntentOperations> execute(IntentOperations operations) { public void execute(IntentOperations operations) {
throw new UnsupportedOperationException("execute() is not implemented yet"); batchService.addIntentOperations(operations);
} }
@Override @Override
@ -261,29 +261,25 @@ public class IntentManager
/** /**
* Compiles the specified intent. * Compiles the specified intent.
* *
* @param intent intent to be compiled * @param update intent update
*/ */
private void executeCompilingPhase(Intent intent) { private void executeCompilingPhase(IntentUpdate update) {
Intent intent = update.newIntent();
// Indicate that the intent is entering the compiling phase. // Indicate that the intent is entering the compiling phase.
store.setState(intent, COMPILING); update.setState(intent, COMPILING);
try { try {
// Compile the intent into installable derivatives. // Compile the intent into installable derivatives.
List<Intent> installable = compileIntent(intent); List<Intent> installables = compileIntent(intent, update);
// If all went well, associate the resulting list of installable // If all went well, associate the resulting list of installable
// intents with the top-level intent and proceed to install. // intents with the top-level intent and proceed to install.
store.setInstallableIntents(intent.id(), installable); update.setInstallables(installables);
executeInstallingPhase(intent); } catch (IntentException e) {
} catch (Exception e) {
log.warn("Unable to compile intent {} due to:", intent.id(), e); log.warn("Unable to compile intent {} due to:", intent.id(), e);
// If compilation failed, mark the intent as failed. // If compilation failed, mark the intent as failed.
IntentEvent event = store.setState(intent, FAILED); update.setState(intent, FAILED);
if (event != null) {
eventDispatcher.post(event);
}
} }
} }
@ -293,17 +289,18 @@ public class IntentManager
* @param intent intent * @param intent intent
* @return result of compilation * @return result of compilation
*/ */
private List<Intent> compileIntent(Intent intent) { private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
if (intent.isInstallable()) { if (intent.isInstallable()) {
return ImmutableList.of(intent); return ImmutableList.of(intent);
} }
registerSubclassCompilerIfNeeded(intent);
List<Intent> previous = update.oldInstallables();
// FIXME: get previous resources
List<Intent> installable = new ArrayList<>(); List<Intent> installable = new ArrayList<>();
// TODO do we need to registerSubclassCompiler? for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
for (Intent compiled : getCompiler(intent).compile(intent)) { installable.addAll(compileIntent(compiled, update));
installable.addAll(compileIntent(compiled));
} }
return installable; return installable;
} }
@ -311,63 +308,110 @@ public class IntentManager
* Installs all installable intents associated with the specified top-level * Installs all installable intents associated with the specified top-level
* intent. * intent.
* *
* @param intent intent to be installed * @param update intent update
*/ */
private void executeInstallingPhase(Intent intent) { private void executeInstallingPhase(IntentUpdate update) {
// Indicate that the intent is entering the installing phase. if (update.newInstallables() == null) {
store.setState(intent, INSTALLING); //no failed intents allowed past this point...
return;
List<FlowRuleBatchOperation> installWork = Lists.newArrayList();
try {
List<Intent> installables = store.getInstallableIntents(intent.id());
if (installables != null) {
for (Intent installable : installables) {
registerSubclassInstallerIfNeeded(installable);
trackerService.addTrackedResources(intent.id(),
installable.resources());
List<FlowRuleBatchOperation> batch = getInstaller(installable).install(installable);
installWork.addAll(batch);
}
}
// FIXME we have to wait for the installable intents
//eventDispatcher.post(store.setState(intent, INSTALLED));
monitorExecutor.execute(new IntentInstallMonitor(intent, installWork, INSTALLED));
} catch (Exception e) {
log.warn("Unable to install intent {} due to:", intent.id(), e);
uninstallIntent(intent, RECOMPILING);
// If compilation failed, kick off the recompiling phase.
// FIXME
//executeRecompilingPhase(intent);
} }
// Indicate that the intent is entering the installing phase.
update.setState(update.newIntent(), INSTALLING);
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
for (Intent installable : update.newInstallables()) {
registerSubclassInstallerIfNeeded(installable);
trackerService.addTrackedResources(update.newIntent().id(),
installable.resources());
try {
batches.addAll(getInstaller(installable).install(installable));
} catch (IntentException e) {
log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
//FIXME we failed... intent should be recompiled
// TODO: remove resources
// recompile!!!
}
}
update.setBatches(batches);
}
/**
* Uninstalls the specified intent by uninstalling all of its associated
* installable derivatives.
*
* @param update intent update
*/
private void executeWithdrawingPhase(IntentUpdate update) {
if (!update.oldIntent().equals(update.newIntent())) {
update.setState(update.oldIntent(), WITHDRAWING);
} // else newIntent is FAILED
uninstallIntent(update);
// If all went well, disassociate the top-level intent with its
// installable derivatives and mark it as withdrawn.
// FIXME need to clean up
//store.removeInstalledIntents(intent.id());
}
/**
* Uninstalls all installable intents associated with the given intent.
*
* @param update intent update
*/
//FIXME: need to handle next state properly
private void uninstallIntent(IntentUpdate update) {
if (update.oldInstallables == null) {
return;
}
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
for (Intent installable : update.oldInstallables()) {
trackerService.removeTrackedResources(update.oldIntent().id(),
installable.resources());
try {
batches.addAll(getInstaller(installable).uninstall(installable));
} catch (IntentException e) {
log.warn("Unable to uninstall intent {} due to:", update.oldIntent().id(), e);
// TODO: this should never happen. but what if it does?
}
}
update.setBatches(batches);
// FIXME: next state for old is WITHDRAWN or FAILED
} }
/** /**
* Recompiles the specified intent. * Recompiles the specified intent.
* *
* @param intent intent to be recompiled * @param update intent update
*/ */
private void executeRecompilingPhase(Intent intent) { // FIXME: update this to work
private void executeRecompilingPhase(IntentUpdate update) {
Intent intent = update.newIntent();
// Indicate that the intent is entering the recompiling phase. // Indicate that the intent is entering the recompiling phase.
store.setState(intent, RECOMPILING); store.setState(intent, RECOMPILING);
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
try { try {
// Compile the intent into installable derivatives. // Compile the intent into installable derivatives.
List<Intent> installable = compileIntent(intent); List<Intent> installable = compileIntent(intent, update);
// If all went well, compare the existing list of installable // If all went well, compare the existing list of installable
// intents with the newly compiled list. If they are the same, // intents with the newly compiled list. If they are the same,
// bail, out since the previous approach was determined not to // bail, out since the previous approach was determined not to
// be viable. // be viable.
// FIXME do we need this?
List<Intent> originalInstallable = store.getInstallableIntents(intent.id()); List<Intent> originalInstallable = store.getInstallableIntents(intent.id());
//FIXME let's be smarter about how we perform the update
//batches.addAll(uninstallIntent(intent, null));
if (Objects.equals(originalInstallable, installable)) { if (Objects.equals(originalInstallable, installable)) {
eventDispatcher.post(store.setState(intent, FAILED)); eventDispatcher.post(store.setState(intent, FAILED));
} else { } else {
// Otherwise, re-associate the newly compiled installable intents // Otherwise, re-associate the newly compiled installable intents
// with the top-level intent and kick off installing phase. // with the top-level intent and kick off installing phase.
store.setInstallableIntents(intent.id(), installable); store.setInstallableIntents(intent.id(), installable);
executeInstallingPhase(intent); // FIXME commented out for now
//batches.addAll(executeInstallingPhase(update));
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("Unable to recompile intent {} due to:", intent.id(), e); log.warn("Unable to recompile intent {} due to:", intent.id(), e);
@ -378,45 +422,38 @@ public class IntentManager
} }
/** /**
* Uninstalls the specified intent by uninstalling all of its associated * Withdraws the old intent and installs the new intent as one operation.
* installable derivatives.
* *
* @param intent intent to be installed * @param update intent update
*/ */
private void executeWithdrawingPhase(Intent intent) { private void executeReplacementPhase(IntentUpdate update) {
// Indicate that the intent is being withdrawn. checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
store.setState(intent, WITHDRAWING); "Old and New Intent must have equivalent installable intents.");
uninstallIntent(intent, WITHDRAWN); if (!update.oldIntent().equals(update.newIntent())) {
// only set the old intent's state if it is different
// If all went well, disassociate the top-level intent with its update.setState(update.oldIntent(), WITHDRAWING);
// installable derivatives and mark it as withdrawn.
// FIXME need to clean up
//store.removeInstalledIntents(intent.id());
// FIXME
//eventDispatcher.post(store.setState(intent, WITHDRAWN));
}
/**
* Uninstalls all installable intents associated with the given intent.
*
* @param intent intent to be uninstalled
*/
private void uninstallIntent(Intent intent, IntentState nextState) {
List<FlowRuleBatchOperation> uninstallWork = Lists.newArrayList();
try {
List<Intent> installables = store.getInstallableIntents(intent.id());
if (installables != null) {
for (Intent installable : installables) {
trackerService.removeTrackedResources(intent.id(),
installable.resources());
List<FlowRuleBatchOperation> batches = getInstaller(installable).uninstall(installable);
uninstallWork.addAll(batches);
}
}
monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallWork, nextState));
} catch (IntentException e) {
log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
} }
update.setState(update.newIntent(), INSTALLING);
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
for (int i = 0; i < update.oldInstallables().size(); i++) {
Intent oldInstallable = update.oldInstallables().get(i);
Intent newInstallable = update.newInstallables().get(i);
if (oldInstallable.equals(newInstallable)) {
continue;
}
checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
"Installable Intent type mismatch.");
trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
try {
batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
} catch (IntentException e) {
log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
//FIXME... we failed. need to uninstall (if same) or revert (if different)
}
}
update.setBatches(batches);
} }
/** /**
@ -474,9 +511,6 @@ public class IntentManager
@Override @Override
public void notify(IntentEvent event) { public void notify(IntentEvent event) {
eventDispatcher.post(event); eventDispatcher.post(event);
if (event.type() == IntentEvent.Type.SUBMITTED) {
executor.execute(new IntentTask(COMPILING, event.subject()));
}
} }
} }
@ -486,82 +520,221 @@ public class IntentManager
public void triggerCompile(Iterable<IntentId> intentIds, public void triggerCompile(Iterable<IntentId> intentIds,
boolean compileAllFailed) { boolean compileAllFailed) {
// Attempt recompilation of the specified intents first. // Attempt recompilation of the specified intents first.
for (IntentId intentId : intentIds) { IntentOperations.Builder builder = IntentOperations.builder();
Intent intent = getIntent(intentId); for (IntentId id : intentIds) {
uninstallIntent(intent, RECOMPILING); builder.addUpdateOperation(id);
//FIXME
//executeRecompilingPhase(intent);
} }
if (compileAllFailed) { if (compileAllFailed) {
// If required, compile all currently failed intents. // If required, compile all currently failed intents.
for (Intent intent : getIntents()) { for (Intent intent : getIntents()) {
if (getIntentState(intent.id()) == FAILED) { if (getIntentState(intent.id()) == FAILED) {
executeCompilingPhase(intent); builder.addUpdateOperation(intent.id());
} }
} }
} }
execute(builder.build());
} }
} }
/**
* TODO.
* @param op intent operation
* @return intent update
*/
private IntentUpdate processIntentOperation(IntentOperation op) {
IntentUpdate update = new IntentUpdate(op);
if (update.newIntent() != null) {
executeCompilingPhase(update);
}
if (update.oldInstallables() != null && update.newInstallables() != null) {
executeReplacementPhase(update);
} else if (update.newInstallables() != null) {
executeInstallingPhase(update);
} else if (update.oldInstallables() != null) {
executeWithdrawingPhase(update);
} else {
if (update.oldIntent() != null) {
// TODO this shouldn't happen
return update; //FIXME
}
if (update.newIntent() != null) {
// TODO assert that next state is failed
return update; //FIXME
}
}
return update;
}
// TODO comments...
private class IntentUpdate {
private final IntentOperation op;
private final Intent oldIntent;
private final Intent newIntent;
private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
private final List<Intent> oldInstallables;
private List<Intent> newInstallables;
private List<FlowRuleBatchOperation> batches;
IntentUpdate(IntentOperation op) {
this.op = op;
switch (op.type()) {
case SUBMIT:
newIntent = op.intent();
oldIntent = null;
break;
case WITHDRAW:
newIntent = null;
oldIntent = store.getIntent(op.intentId());
break;
case REPLACE:
newIntent = op.intent();
oldIntent = store.getIntent(op.intentId());
break;
case UPDATE:
oldIntent = store.getIntent(op.intentId());
newIntent = oldIntent; //InnerAssignment: Inner assignments should be avoided.
break;
default:
oldIntent = null;
newIntent = null;
break;
}
// add new intent to store (if required)
if (newIntent != null) {
IntentEvent event = store.createIntent(newIntent);
if (event != null) {
eventDispatcher.post(event);
}
}
// fetch the old intent's installables from the store
if (oldIntent != null) {
oldInstallables = store.getInstallableIntents(oldIntent.id());
// TODO: remove intent from store after uninstall
} else {
oldInstallables = null;
}
}
Intent oldIntent() {
return oldIntent;
}
Intent newIntent() {
return newIntent;
}
List<Intent> oldInstallables() {
return oldInstallables;
}
List<Intent> newInstallables() {
return newInstallables;
}
void setInstallables(List<Intent> installables) {
newInstallables = installables;
store.setInstallableIntents(newIntent.id(), installables);
}
List<FlowRuleBatchOperation> batches() {
return batches;
}
void setBatches(List<FlowRuleBatchOperation> batches) {
this.batches = batches;
}
IntentState getState(Intent intent) {
return stateMap.get(intent);
}
void setState(Intent intent, IntentState newState) {
// TODO: clean this up, or set to debug
IntentState oldState = stateMap.get(intent);
log.info("intent id: {}, old state: {}, new state: {}",
intent.id(), oldState, newState);
stateMap.put(intent, newState);
IntentEvent event = store.setState(intent, newState);
if (event != null) {
eventDispatcher.post(event);
}
}
Map<Intent, IntentState> stateMap() {
return stateMap;
}
}
private static List<FlowRuleBatchOperation> mergeBatches(Map<IntentOperation,
IntentUpdate> intentUpdates) {
//TODO test this.
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
for (IntentUpdate update : intentUpdates.values()) {
if (update.batches() == null) {
continue;
}
int i = 0;
for (FlowRuleBatchOperation batch : update.batches()) {
if (i == batches.size()) {
batches.add(batch);
} else {
FlowRuleBatchOperation existing = batches.get(i);
existing.addAll(batch);
}
i++;
}
}
return batches;
}
// Auxiliary runnable to perform asynchronous tasks. // Auxiliary runnable to perform asynchronous tasks.
private class IntentTask implements Runnable { private class IntentTask implements Runnable {
private final IntentState state; private final IntentOperations operations;
private final Intent intent;
public IntentTask(IntentState state, Intent intent) { public IntentTask(IntentOperations operations) {
this.state = state; this.operations = operations;
this.intent = intent;
} }
@Override @Override
public void run() { public void run() {
if (state == COMPILING) { Map<IntentOperation, IntentUpdate> intentUpdates = Maps.newHashMap();
executeCompilingPhase(intent); for (IntentOperation op : operations.operations()) {
} else if (state == RECOMPILING) { intentUpdates.put(op, processIntentOperation(op));
executeRecompilingPhase(intent);
} else if (state == WITHDRAWING) {
executeWithdrawingPhase(intent);
} }
List<FlowRuleBatchOperation> batches = mergeBatches(intentUpdates);
monitorExecutor.execute(new IntentInstallMonitor(operations, intentUpdates, batches));
} }
} }
private class IntentInstallMonitor implements Runnable { private class IntentInstallMonitor implements Runnable {
private final Intent intent; private static final long TIMEOUT = 5000; // ms
private final IntentOperations ops;
private final Map<IntentOperation, IntentUpdate> intentUpdateMap;
private final List<FlowRuleBatchOperation> work; private final List<FlowRuleBatchOperation> work;
private final List<Future<CompletedBatchOperation>> futures; private Future<CompletedBatchOperation> future;
private final IntentState nextState; private final long startTime = System.currentTimeMillis();
private final long endTime = startTime + TIMEOUT;
public IntentInstallMonitor(Intent intent, public IntentInstallMonitor(IntentOperations ops,
List<FlowRuleBatchOperation> work, Map<IntentOperation, IntentUpdate> intentUpdateMap,
IntentState nextState) { List<FlowRuleBatchOperation> work) {
this.intent = intent; this.ops = ops;
this.intentUpdateMap = intentUpdateMap;
this.work = work; this.work = work;
// TODO how many Futures can be outstanding? one? future = applyNextBatch();
this.futures = Lists.newLinkedList();
this.nextState = nextState;
// TODO need to kick off the first batch sometime, why not now?
futures.add(applyNextBatch());
} }
/** /**
* Update the intent store with the next status for this intent. * Applies the next batch, and returns the future.
*/ *
private void updateIntent() { * @return Future for next batch
if (nextState == RECOMPILING) {
executor.execute(new IntentTask(nextState, intent));
} else if (nextState == INSTALLED || nextState == WITHDRAWN) {
eventDispatcher.post(store.setState(intent, nextState));
} else {
log.warn("Invalid next intent state {} for intent {}", nextState, intent);
}
}
/**
* Applies the next batch.
*/ */
private Future<CompletedBatchOperation> applyNextBatch() { private Future<CompletedBatchOperation> applyNextBatch() {
if (work.isEmpty()) { if (work.isEmpty()) {
@ -571,46 +744,125 @@ public class IntentManager
return flowRuleService.applyBatch(batch); return flowRuleService.applyBatch(batch);
} }
/**
* Update the intent store with the next status for this intent.
*/
private void updateIntents() {
// FIXME we assume everything passes for now.
for (IntentUpdate update : intentUpdateMap.values()) {
for (Intent intent : update.stateMap().keySet()) {
switch (update.getState(intent)) {
case INSTALLING:
update.setState(intent, INSTALLED);
break;
case WITHDRAWING:
update.setState(intent, WITHDRAWN);
// Fall-through
case FAILED:
store.removeInstalledIntents(intent.id());
break;
case SUBMITTED:
case COMPILING:
case RECOMPILING:
case WITHDRAWN:
case INSTALLED:
default:
//FIXME clean this up (we shouldn't ever get here)
log.warn("Bad state: {} for {}", update.getState(intent), intent);
break;
}
}
}
/*
for (IntentOperation op : ops.operations()) {
switch (op.type()) {
case SUBMIT:
store.setState(op.intent(), INSTALLED);
break;
case WITHDRAW:
Intent intent = store.getIntent(op.intentId());
store.setState(intent, WITHDRAWN);
break;
case REPLACE:
store.setState(op.intent(), INSTALLED);
intent = store.getIntent(op.intentId());
store.setState(intent, WITHDRAWN);
break;
case UPDATE:
intent = store.getIntent(op.intentId());
store.setState(intent, INSTALLED);
break;
default:
break;
}
}
*/
/*
if (nextState == RECOMPILING) {
eventDispatcher.post(store.setState(intent, FAILED));
// FIXME try to recompile
// executor.execute(new IntentTask(nextState, intent));
} else if (nextState == INSTALLED || nextState == WITHDRAWN) {
eventDispatcher.post(store.setState(intent, nextState));
} else {
log.warn("Invalid next intent state {} for intent {}", nextState, intent);
}*/
}
/** /**
* Iterate through the pending futures, and remove them when they have completed. * Iterate through the pending futures, and remove them when they have completed.
*/ */
private void processFutures() { private void processFutures() {
List<Future<CompletedBatchOperation>> newFutures = Lists.newArrayList(); if (future == null) {
for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) { return; //FIXME look at this
Future<CompletedBatchOperation> future = i.next(); }
try { try {
// TODO: we may want to get the future here and go back to the future. CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS); if (completed.isSuccess()) {
if (completed.isSuccess()) { future = applyNextBatch();
Future<CompletedBatchOperation> newFuture = applyNextBatch(); } else {
if (newFuture != null) { // TODO check if future succeeded and if not report fail items
// we'll add this later so that we don't get a ConcurrentModException log.warn("Failed items: {}", completed.failedItems());
newFutures.add(newFuture); // FIXME revert.... by submitting a new batch
} //uninstallIntent(intent, RECOMPILING);
} else { }
// TODO check if future succeeded and if not report fail items } catch (TimeoutException | InterruptedException | ExecutionException te) {
log.warn("Failed items: {}", completed.failedItems()); //TODO look into error message
// TODO revert.... log.debug("Intallations of intent {} is still pending", ops);
//uninstallIntent(intent, RECOMPILING);
}
i.remove();
} catch (TimeoutException | InterruptedException | ExecutionException te) {
log.debug("Intallations of intent {} is still pending", intent);
}
} }
futures.addAll(newFutures);
} }
@Override @Override
public void run() { public void run() {
processFutures(); processFutures();
if (futures.isEmpty()) { if (future == null) {
// woohoo! we are done! // woohoo! we are done!
updateIntent(); updateIntents();
batchService.removeIntentOperations(ops);
} else if (endTime < System.currentTimeMillis()) {
log.warn("Install request timed out");
// future.cancel(true);
// TODO retry and/or report the failure
} else { } else {
// resubmit ourselves if we are not done yet // resubmit ourselves if we are not done yet
monitorExecutor.submit(this); monitorExecutor.submit(this);
} }
} }
} }
private class InternalBatchDelegate implements IntentBatchDelegate {
@Override
public void execute(IntentOperations operations) {
log.info("Execute operations: {}", operations);
//FIXME: perhaps we want to track this task so that we can cancel it.
executor.execute(new IntentTask(operations));
}
@Override
public void cancel(IntentOperations operations) {
//FIXME: implement this
log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
}
}
} }

View File

@ -111,6 +111,13 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec
return Lists.newArrayList(new FlowRuleBatchOperation(rules)); return Lists.newArrayList(new FlowRuleBatchOperation(rules));
} }
@Override
public List<FlowRuleBatchOperation> replace(LinkCollectionIntent intent,
LinkCollectionIntent newIntent) {
// FIXME: implement
return null;
}
/** /**
* Creates a FlowRuleBatchEntry based on the provided parameters. * Creates a FlowRuleBatchEntry based on the provided parameters.
* *

View File

@ -29,6 +29,7 @@ import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.LinkCollectionIntent; import org.onlab.onos.net.intent.LinkCollectionIntent;
import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
import org.onlab.onos.net.intent.PointToPointIntent; import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import org.onlab.onos.net.topology.PathService; import org.onlab.onos.net.topology.PathService;
import java.util.Arrays; import java.util.Arrays;
@ -61,7 +62,8 @@ public class MultiPointToSinglePointIntentCompiler
} }
@Override @Override
public List<Intent> compile(MultiPointToSinglePointIntent intent) { public List<Intent> compile(MultiPointToSinglePointIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
Set<Link> links = new HashSet<>(); Set<Link> links = new HashSet<>();
for (ConnectPoint ingressPoint : intent.ingressPoints()) { for (ConnectPoint ingressPoint : intent.ingressPoints()) {

View File

@ -29,6 +29,7 @@ import org.onlab.onos.net.intent.IntentCompiler;
import org.onlab.onos.net.intent.IntentExtensionService; import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.OpticalConnectivityIntent; import org.onlab.onos.net.intent.OpticalConnectivityIntent;
import org.onlab.onos.net.intent.OpticalPathIntent; import org.onlab.onos.net.intent.OpticalPathIntent;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import org.onlab.onos.net.topology.LinkWeight; import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.Topology; import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyEdge; import org.onlab.onos.net.topology.TopologyEdge;
@ -60,7 +61,9 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
} }
@Override @Override
public List<Intent> compile(OpticalConnectivityIntent intent) { public List<Intent> compile(OpticalConnectivityIntent intent,
List<Intent> installable,
Set<LinkResourceAllocations> resources) {
// TODO: compute multiple paths using the K-shortest path algorithm // TODO: compute multiple paths using the K-shortest path algorithm
Path path = calculateOpticalPath(intent.getSrcConnectPoint(), intent.getDst()); Path path = calculateOpticalPath(intent.getSrcConnectPoint(), intent.getDst());
Intent newIntent = new OpticalPathIntent(intent.appId(), Intent newIntent = new OpticalPathIntent(intent.appId(),

View File

@ -104,6 +104,13 @@ public class OpticalPathIntentInstaller implements IntentInstaller<OpticalPathIn
return generateRules(intent, allocations, FlowRuleOperation.REMOVE); return generateRules(intent, allocations, FlowRuleOperation.REMOVE);
} }
@Override
public List<FlowRuleBatchOperation> replace(OpticalPathIntent intent,
OpticalPathIntent newIntent) {
// FIXME: implement this
return null;
}
private LinkResourceAllocations assignWavelength(OpticalPathIntent intent) { private LinkResourceAllocations assignWavelength(OpticalPathIntent intent) {
LinkResourceRequest.Builder request = DefaultLinkResourceRequest.builder(intent.id(), LinkResourceRequest.Builder request = DefaultLinkResourceRequest.builder(intent.id(),
intent.path().links()) intent.path().links())

View File

@ -131,6 +131,15 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
return Lists.newArrayList(new FlowRuleBatchOperation(rules)); return Lists.newArrayList(new FlowRuleBatchOperation(rules));
} }
@Override
public List<FlowRuleBatchOperation> replace(PathIntent oldIntent, PathIntent newIntent) {
// FIXME: implement this
List<FlowRuleBatchOperation> batches = Lists.newArrayList();
batches.addAll(uninstall(oldIntent));
batches.addAll(install(newIntent));
return batches;
}
/** /**
* Allocate resources required for an intent. * Allocate resources required for an intent.
* *
@ -147,7 +156,7 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
return request.resources().isEmpty() ? null : resourceService.requestResources(request); return request.resources().isEmpty() ? null : resourceService.requestResources(request);
} }
// TODO refactor below this line... ---------------------------- // FIXME refactor below this line... ----------------------------
/** /**
* Generates the series of MatchActionOperations from the * Generates the series of MatchActionOperations from the

View File

@ -28,9 +28,11 @@ import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.PathIntent; import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.intent.PointToPointIntent; import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.net.provider.ProviderId; import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.onlab.onos.net.Link.Type.DIRECT; import static org.onlab.onos.net.Link.Type.DIRECT;
@ -57,7 +59,9 @@ public class PointToPointIntentCompiler
} }
@Override @Override
public List<Intent> compile(PointToPointIntent intent) { public List<Intent> compile(PointToPointIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
ConnectPoint ingressPoint = intent.ingressPoint(); ConnectPoint ingressPoint = intent.ingressPoint();
ConnectPoint egressPoint = intent.egressPoint(); ConnectPoint egressPoint = intent.egressPoint();

View File

@ -87,7 +87,7 @@ public class PathConstraintCalculationTest {
constraints); constraints);
final PointToPointIntentCompiler compiler = makeCompiler(resourceService); final PointToPointIntentCompiler compiler = makeCompiler(resourceService);
return compiler.compile(intent); return compiler.compile(intent, null, null);
} }
/** /**

View File

@ -121,7 +121,7 @@ public class TestHostToHostIntentCompiler {
HostToHostIntentCompiler compiler = makeCompiler(hops); HostToHostIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(Matchers.notNullValue())); assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(2)); assertThat(result, hasSize(2));
Intent forwardResultIntent = result.get(0); Intent forwardResultIntent = result.get(0);

View File

@ -137,7 +137,7 @@ public class TestMultiPointToSinglePointIntentCompiler {
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(Matchers.notNullValue())); assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
Intent resultIntent = result.get(0); Intent resultIntent = result.get(0);
@ -172,7 +172,7 @@ public class TestMultiPointToSinglePointIntentCompiler {
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(notNullValue())); assertThat(result, is(notNullValue()));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
Intent resultIntent = result.get(0); Intent resultIntent = result.get(0);
@ -205,7 +205,7 @@ public class TestMultiPointToSinglePointIntentCompiler {
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops); MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(notNullValue())); assertThat(result, is(notNullValue()));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
Intent resultIntent = result.get(0); Intent resultIntent = result.get(0);

View File

@ -93,7 +93,7 @@ public class TestPointToPointIntentCompiler {
PointToPointIntentCompiler compiler = makeCompiler(hops); PointToPointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(Matchers.notNullValue())); assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
Intent forwardResultIntent = result.get(0); Intent forwardResultIntent = result.get(0);
@ -126,7 +126,7 @@ public class TestPointToPointIntentCompiler {
PointToPointIntentCompiler compiler = makeCompiler(hops); PointToPointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue())); assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent); List<Intent> result = compiler.compile(intent, null, null);
assertThat(result, is(Matchers.notNullValue())); assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
Intent reverseResultIntent = result.get(0); Intent reverseResultIntent = result.get(0);
@ -157,7 +157,7 @@ public class TestPointToPointIntentCompiler {
String[] hops = {"1"}; String[] hops = {"1"};
PointToPointIntentCompiler sut = makeCompiler(hops); PointToPointIntentCompiler sut = makeCompiler(hops);
List<Intent> compiled = sut.compile(intent); List<Intent> compiled = sut.compile(intent, null, null);
assertThat(compiled, hasSize(1)); assertThat(compiled, hasSize(1));
assertThat(compiled.get(0), is(instanceOf(PathIntent.class))); assertThat(compiled.get(0), is(instanceOf(PathIntent.class)));

View File

@ -0,0 +1,81 @@
/*
* Copyright 2014 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.onlab.onos.store.trivial.impl;
import com.google.common.collect.ImmutableSet;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.net.intent.IntentBatchDelegate;
import org.onlab.onos.net.intent.IntentBatchService;
import org.onlab.onos.net.intent.IntentOperations;
import org.slf4j.Logger;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true)
@Service
public class SimpleIntentBatchQueue implements IntentBatchService {
private final Logger log = getLogger(getClass());
private final Set<IntentOperations> pendingBatches = new HashSet<>();
private IntentBatchDelegate delegate;
@Activate
public void activate() {
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public void addIntentOperations(IntentOperations operations) {
checkState(delegate != null, "No delegate set");
pendingBatches.add(operations);
delegate.execute(operations);
}
@Override
public void removeIntentOperations(IntentOperations operations) {
pendingBatches.remove(operations);
}
@Override
public Set<IntentOperations> getIntentOperations() {
return ImmutableSet.copyOf(pendingBatches);
}
@Override
public void setDelegate(IntentBatchDelegate delegate) {
this.delegate = checkNotNull(delegate, "Delegate cannot be null");
}
@Override
public void unsetDelegate(IntentBatchDelegate delegate) {
if (this.delegate != null && this.delegate.equals(delegate)) {
this.delegate = null;
}
}
}

View File

@ -33,7 +33,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static org.onlab.onos.net.intent.IntentState.*; import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
import static org.slf4j.LoggerFactory.getLogger; import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true) @Component(immediate = true)
@ -45,8 +45,8 @@ public class SimpleIntentStore
private final Logger log = getLogger(getClass()); private final Logger log = getLogger(getClass());
private final Map<IntentId, Intent> intents = new ConcurrentHashMap<>(); private final Map<IntentId, Intent> intents = new ConcurrentHashMap<>();
private final Map<IntentId, IntentState> states = new ConcurrentHashMap<>(); private final Map<IntentId, IntentState> states = new ConcurrentHashMap<>();
private final Map<IntentId, List<Intent>> installable = private final Map<IntentId, List<Intent>> installable = new ConcurrentHashMap<>();
new ConcurrentHashMap<>();
@Activate @Activate
public void activate() { public void activate() {
@ -60,6 +60,9 @@ public class SimpleIntentStore
@Override @Override
public IntentEvent createIntent(Intent intent) { public IntentEvent createIntent(Intent intent) {
if (intents.containsKey(intent.id())) {
return null;
}
intents.put(intent.id(), intent); intents.put(intent.id(), intent);
return this.setState(intent, IntentState.SUBMITTED); return this.setState(intent, IntentState.SUBMITTED);
} }