ONOS-2513 Fix entire MP2SP intent failing on partial connectivity loss
* Added PartialFailureContraint to MP2SP intent to allow partial connectivity.
This means the intent remains installed as long as at least one ingress point
can reach the egress point.
* Intents with this constraint are recompiled on ObjectiveTracker triggers
even if not in FAILED state
* MP2SP intent compiler can compute a partial tree if constraint is set
* ObjectiveTracker recompiles intents on any link event
* SDN-IP MP2SP intents now use PartialFailureConstraint
Ported from onos-1.2 branch.
Change-Id: I32eaa198fae1dfba021d9251c8f855573f0e1d7d
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
index cad8752..cde59d1 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -15,6 +15,40 @@
*/
package org.onosproject.sdnip;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.FibListener;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.config.BgpPeer;
+import org.onosproject.routing.config.Interface;
+import org.onosproject.routing.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -28,38 +62,6 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
-import org.onosproject.net.intent.Key;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.routing.FibListener;
-import org.onosproject.routing.FibUpdate;
-import org.onosproject.routing.IntentRequestListener;
-import org.onosproject.routing.config.BgpPeer;
-import org.onosproject.routing.config.Interface;
-import org.onosproject.routing.config.RoutingConfigurationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -70,6 +72,8 @@
public class IntentSynchronizer implements FibListener, IntentRequestListener {
private static final int PRIORITY_OFFSET = 100;
private static final int PRIORITY_MULTIPLIER = 5;
+ protected static final ImmutableList<Constraint> CONSTRAINTS
+ = ImmutableList.of(new PartialFailureConstraint());
private static final Logger log =
LoggerFactory.getLogger(IntentSynchronizer.class);
@@ -375,6 +379,7 @@
.ingressPoints(ingressPorts)
.egressPoint(egressPort)
.priority(priority)
+ .constraints(CONSTRAINTS)
.build();
}
@@ -425,6 +430,7 @@
.ingressPoints(ingressPoints)
.egressPoint(egressPoint)
.priority(priority)
+ .constraints(CONSTRAINTS)
.build();
log.trace("Generates ConnectivityInternetToHost intent {}", intent);
@@ -923,6 +929,7 @@
.ingressPoints(ingressPoints)
.egressPoint(dstConnectPoint)
.priority(priority)
+ .constraints(CONSTRAINTS)
.build();
log.trace("Generates ConnectivityHostToHost = {} ", intent);
@@ -950,6 +957,7 @@
.ingressPoints(ingressPoints)
.egressPoint(existingIntent.egressPoint())
.priority(existingIntent.priority())
+ .constraints(CONSTRAINTS)
.build();
log.trace("Update an existing MultiPointToSinglePointIntent "
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index a3f468c..2a52539 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -239,6 +239,7 @@
.treatment(treatmentBuilder.build())
.ingressPoints(ingressPoints)
.egressPoint(SW1_ETH1)
+ .constraints(IntentSynchronizer.CONSTRAINTS)
.build();
// Setup the expected intents
@@ -301,6 +302,7 @@
.treatment(treatmentBuilder.build())
.ingressPoints(ingressPoints)
.egressPoint(SW4_ETH1)
+ .constraints(IntentSynchronizer.CONSTRAINTS)
.build();
// Setup the expected intents
@@ -371,6 +373,7 @@
.treatment(treatmentBuilderNew.build())
.ingressPoints(ingressPointsNew)
.egressPoint(SW2_ETH1)
+ .constraints(IntentSynchronizer.CONSTRAINTS)
.build();
// Set up test expectation
@@ -609,6 +612,7 @@
.treatment(treatmentBuilder.build())
.ingressPoints(ingressPoints)
.egressPoint(egressPoint)
+ .constraints(IntentSynchronizer.CONSTRAINTS)
.build();
return intent;
}
diff --git a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
index d22f147..42c2662 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -40,6 +40,7 @@
import org.onosproject.net.intent.constraint.BandwidthConstraint;
import org.onosproject.net.intent.constraint.LambdaConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
import org.onosproject.net.resource.link.BandwidthResource;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
@@ -130,6 +131,10 @@
required = false, multiValued = false)
private String intentKey = null;
+ @Option(name = "--partial", description = "Allow partial installation",
+ required = false, multiValued = false)
+ private boolean partial = false;
+
// Treatments
@Option(name = "--setEthSrc", description = "Rewrite Source MAC Address",
@@ -350,6 +355,10 @@
}
constraints.add(new LinkTypeConstraint(lambda, Link.Type.OPTICAL));
+ if (partial) {
+ constraints.add(new PartialFailureConstraint());
+ }
+
return constraints;
}
diff --git a/core/api/src/main/java/org/onosproject/net/intent/constraint/PartialFailureConstraint.java b/core/api/src/main/java/org/onosproject/net/intent/constraint/PartialFailureConstraint.java
new file mode 100644
index 0000000..827859b
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/constraint/PartialFailureConstraint.java
@@ -0,0 +1,49 @@
+/*
+ * 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.constraint;
+
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.resource.link.LinkResourceService;
+
+/**
+ * A constraint that allows intents that can only be partially compiled
+ * (i.e. MultiPointToSinglePointIntent or SinglePointToMultiPointIntent)
+ * to be installed when some endpoints or paths are not found.
+ */
+public class PartialFailureConstraint implements Constraint {
+ @Override
+ public double cost(Link link, LinkResourceService resourceService) {
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Path path, LinkResourceService resourceService) {
+ return true;
+ }
+
+ public static boolean intentAllowsPartialFailure(Intent intent) {
+ if (intent instanceof ConnectivityIntent) {
+ ConnectivityIntent connectivityIntent = (ConnectivityIntent) intent;
+ return connectivityIntent.constraints().stream()
+ .anyMatch(c -> c instanceof PartialFailureConstraint);
+ }
+ return false;
+ }
+}
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 ded3924..d974009 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
@@ -63,6 +63,7 @@
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
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;
import static org.slf4j.LoggerFactory.getLogger;
@@ -85,6 +86,8 @@
private static final EnumSet<IntentState> RECOMPILE
= EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
+ private static final EnumSet<IntentState> WITHDRAW
+ = EnumSet.of(WITHDRAW_REQ, WITHDRAWING, WITHDRAWN);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -252,16 +255,15 @@
submit(intent);
}
- if (compileAllFailed) {
- // If required, compile all currently failed intents.
- for (Intent intent : getIntents()) {
- IntentState state = getIntentState(intent.key());
- if (RECOMPILE.contains(state)) {
- if (state == WITHDRAW_REQ) {
- withdraw(intent);
- } else {
- submit(intent);
- }
+ // If required, compile all currently failed intents.
+ for (Intent intent : getIntents()) {
+ IntentState state = getIntentState(intent.key());
+ if ((compileAllFailed && RECOMPILE.contains(state))
+ || intentAllowsPartialFailure(intent)) {
+ if (WITHDRAW.contains(state)) {
+ withdraw(intent);
+ } else {
+ submit(intent);
}
}
}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
index 6ec60d7..02d9146 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
@@ -276,31 +276,28 @@
delegate.triggerCompile(Collections.emptySet(), true);
} else {
- Set<Key> toBeRecompiled = new HashSet<>();
- boolean recompileOnly = true;
+ Set<Key> intentsToRecompile = new HashSet<>();
+ boolean dontRecompileAllFailedIntents = true;
// Scan through the list of reasons and keep accruing all
// intents that need to be recompiled.
for (Event reason : event.reasons()) {
if (reason instanceof LinkEvent) {
LinkEvent linkEvent = (LinkEvent) reason;
- if (linkEvent.type() == LINK_REMOVED
- || (linkEvent.type() == LINK_UPDATED &&
- linkEvent.subject().isDurable())) {
- final LinkKey linkKey = linkKey(linkEvent.subject());
- synchronized (intentsByLink) {
- Set<Key> intentKeys = intentsByLink.get(linkKey);
- log.debug("recompile triggered by LinkDown {} {}", linkKey, intentKeys);
- toBeRecompiled.addAll(intentKeys);
- }
+ final LinkKey linkKey = linkKey(linkEvent.subject());
+ synchronized (intentsByLink) {
+ Set<Key> intentKeys = intentsByLink.get(linkKey);
+ log.debug("recompile triggered by LinkEvent {} ({}) for {}",
+ linkKey, linkEvent.type(), intentKeys);
+ intentsToRecompile.addAll(intentKeys);
}
- recompileOnly = recompileOnly &&
+ dontRecompileAllFailedIntents = dontRecompileAllFailedIntents &&
(linkEvent.type() == LINK_REMOVED ||
(linkEvent.type() == LINK_UPDATED &&
linkEvent.subject().isDurable()));
}
}
- delegate.triggerCompile(toBeRecompiled, !recompileOnly);
+ delegate.triggerCompile(intentsToRecompile, !dontRecompileAllFailedIntents);
}
}
}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
index 5c158e0..8fad769 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
@@ -26,13 +26,14 @@
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
+import org.onosproject.net.device.DeviceService;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.LinkCollectionIntent;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.intent.impl.PathNotFoundException;
import org.onosproject.net.resource.link.LinkResourceAllocations;
import org.onosproject.net.topology.PathService;
@@ -42,6 +43,9 @@
import java.util.Map;
import java.util.Set;
+import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
+
+
/**
* An intent compiler for
* {@link org.onosproject.net.intent.MultiPointToSinglePointIntent}.
@@ -56,6 +60,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PathService pathService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
@Activate
public void activate() {
intentManager.registerCompiler(MultiPointToSinglePointIntent.class, this);
@@ -72,23 +79,44 @@
Map<DeviceId, Link> links = new HashMap<>();
ConnectPoint egressPoint = intent.egressPoint();
+ final boolean allowMissingPaths = intentAllowsPartialFailure(intent);
+ boolean partialTree = false;
+ boolean anyMissingPaths = false;
for (ConnectPoint ingressPoint : intent.ingressPoints()) {
if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
- continue;
- }
- Path path = getPath(ingressPoint, intent.egressPoint());
- for (Link link : path.links()) {
- if (links.containsKey(link.src().deviceId())) {
- // We've already reached the existing tree with the first
- // part of this path. Add the merging point with different
- // incoming port, but don't add the remainder of the path
- // in case it differs from the path we already have.
- links.put(link.src().deviceId(), link);
- break;
+ if (deviceService.isAvailable(ingressPoint.deviceId())) {
+ partialTree = true;
+ } else {
+ anyMissingPaths = true;
}
- links.put(link.src().deviceId(), link);
+ continue;
}
+
+ Path path = getPath(ingressPoint, intent.egressPoint());
+ if (path != null) {
+ partialTree = true;
+
+ for (Link link : path.links()) {
+ if (links.containsKey(link.src().deviceId())) {
+ // We've already reached the existing tree with the first
+ // part of this path. Add the merging point with different
+ // incoming port, but don't add the remainder of the path
+ // in case it differs from the path we already have.
+ links.put(link.src().deviceId(), link);
+ break;
+ }
+ links.put(link.src().deviceId(), link);
+ }
+ } else {
+ anyMissingPaths = true;
+ }
+ }
+
+ if (!partialTree) {
+ throw new IntentException("Could not find any paths between ingress and egress points.");
+ } else if (!allowMissingPaths && anyMissingPaths) {
+ throw new IntentException("Missing some paths between ingress and egress ports.");
}
Intent result = LinkCollectionIntent.builder()
@@ -111,12 +139,11 @@
* @param one start of the path
* @param two end of the path
* @return Path between the two
- * @throws org.onosproject.net.intent.impl.PathNotFoundException if a path cannot be found
*/
private Path getPath(ConnectPoint one, ConnectPoint two) {
Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
if (paths.isEmpty()) {
- throw new PathNotFoundException(one.elementId(), two.elementId());
+ return null;
}
// TODO: let's be more intelligent about this eventually
return paths.iterator().next();
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java
index b9b2c54..eb7a393 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java
@@ -20,8 +20,10 @@
import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.Path;
+import org.onosproject.net.device.DeviceServiceAdapter;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.AbstractIntentTest;
@@ -92,6 +94,16 @@
}
/**
+ * Mocks the device service so that a device appears available in the test.
+ */
+ private static class MockDeviceService extends DeviceServiceAdapter {
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return true;
+ }
+ }
+
+ /**
* Creates a MultiPointToSinglePoint intent for a group of ingress points
* and an egress point.
*
@@ -126,6 +138,7 @@
MultiPointToSinglePointIntentCompiler compiler =
new MultiPointToSinglePointIntentCompiler();
compiler.pathService = new MockPathService(hops);
+ compiler.deviceService = new MockDeviceService();
return compiler;
}
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index e609928..fffc86e 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -157,6 +157,7 @@
import org.onosproject.net.intent.constraint.LatencyConstraint;
import org.onosproject.net.intent.constraint.LinkTypeConstraint;
import org.onosproject.net.intent.constraint.ObstacleConstraint;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.newresource.DefaultResource;
@@ -412,6 +413,7 @@
ObstacleConstraint.class,
AnnotationConstraint.class,
BooleanConstraint.class,
+ PartialFailureConstraint.class,
IntentOperation.class,
FlowRuleExtPayLoad.class,
Frequency.class,