Jonathan Hart
Committed by Gerrit Code Review

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
...@@ -15,19 +15,8 @@ ...@@ -15,19 +15,8 @@
15 */ 15 */
16 package org.onosproject.sdnip; 16 package org.onosproject.sdnip;
17 17
18 -import java.util.Collection; 18 +import com.google.common.collect.ImmutableList;
19 -import java.util.HashMap; 19 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
20 -import java.util.HashSet;
21 -import java.util.LinkedList;
22 -import java.util.List;
23 -import java.util.Map;
24 -import java.util.Objects;
25 -import java.util.Set;
26 -import java.util.concurrent.ConcurrentHashMap;
27 -import java.util.concurrent.ExecutorService;
28 -import java.util.concurrent.Executors;
29 -import java.util.concurrent.Semaphore;
30 -
31 import org.onlab.packet.Ethernet; 20 import org.onlab.packet.Ethernet;
32 import org.onlab.packet.IpAddress; 21 import org.onlab.packet.IpAddress;
33 import org.onlab.packet.IpPrefix; 22 import org.onlab.packet.IpPrefix;
...@@ -40,15 +29,17 @@ import org.onosproject.net.flow.DefaultTrafficSelector; ...@@ -40,15 +29,17 @@ import org.onosproject.net.flow.DefaultTrafficSelector;
40 import org.onosproject.net.flow.DefaultTrafficTreatment; 29 import org.onosproject.net.flow.DefaultTrafficTreatment;
41 import org.onosproject.net.flow.TrafficSelector; 30 import org.onosproject.net.flow.TrafficSelector;
42 import org.onosproject.net.flow.TrafficTreatment; 31 import org.onosproject.net.flow.TrafficTreatment;
43 -import org.onosproject.net.flow.criteria.IPCriterion;
44 import org.onosproject.net.flow.criteria.Criterion; 32 import org.onosproject.net.flow.criteria.Criterion;
33 +import org.onosproject.net.flow.criteria.IPCriterion;
45 import org.onosproject.net.host.HostService; 34 import org.onosproject.net.host.HostService;
35 +import org.onosproject.net.intent.Constraint;
46 import org.onosproject.net.intent.Intent; 36 import org.onosproject.net.intent.Intent;
47 import org.onosproject.net.intent.IntentService; 37 import org.onosproject.net.intent.IntentService;
48 import org.onosproject.net.intent.IntentState; 38 import org.onosproject.net.intent.IntentState;
49 import org.onosproject.net.intent.Key; 39 import org.onosproject.net.intent.Key;
50 import org.onosproject.net.intent.MultiPointToSinglePointIntent; 40 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
51 import org.onosproject.net.intent.PointToPointIntent; 41 import org.onosproject.net.intent.PointToPointIntent;
42 +import org.onosproject.net.intent.constraint.PartialFailureConstraint;
52 import org.onosproject.routing.FibListener; 43 import org.onosproject.routing.FibListener;
53 import org.onosproject.routing.FibUpdate; 44 import org.onosproject.routing.FibUpdate;
54 import org.onosproject.routing.IntentRequestListener; 45 import org.onosproject.routing.IntentRequestListener;
...@@ -58,7 +49,18 @@ import org.onosproject.routing.config.RoutingConfigurationService; ...@@ -58,7 +49,18 @@ import org.onosproject.routing.config.RoutingConfigurationService;
58 import org.slf4j.Logger; 49 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory; 50 import org.slf4j.LoggerFactory;
60 51
61 -import com.google.common.util.concurrent.ThreadFactoryBuilder; 52 +import java.util.Collection;
53 +import java.util.HashMap;
54 +import java.util.HashSet;
55 +import java.util.LinkedList;
56 +import java.util.List;
57 +import java.util.Map;
58 +import java.util.Objects;
59 +import java.util.Set;
60 +import java.util.concurrent.ConcurrentHashMap;
61 +import java.util.concurrent.ExecutorService;
62 +import java.util.concurrent.Executors;
63 +import java.util.concurrent.Semaphore;
62 64
63 import static com.google.common.base.Preconditions.checkArgument; 65 import static com.google.common.base.Preconditions.checkArgument;
64 import static com.google.common.base.Preconditions.checkNotNull; 66 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -70,6 +72,8 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -70,6 +72,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
70 public class IntentSynchronizer implements FibListener, IntentRequestListener { 72 public class IntentSynchronizer implements FibListener, IntentRequestListener {
71 private static final int PRIORITY_OFFSET = 100; 73 private static final int PRIORITY_OFFSET = 100;
72 private static final int PRIORITY_MULTIPLIER = 5; 74 private static final int PRIORITY_MULTIPLIER = 5;
75 + protected static final ImmutableList<Constraint> CONSTRAINTS
76 + = ImmutableList.of(new PartialFailureConstraint());
73 77
74 private static final Logger log = 78 private static final Logger log =
75 LoggerFactory.getLogger(IntentSynchronizer.class); 79 LoggerFactory.getLogger(IntentSynchronizer.class);
...@@ -375,6 +379,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { ...@@ -375,6 +379,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
375 .ingressPoints(ingressPorts) 379 .ingressPoints(ingressPorts)
376 .egressPoint(egressPort) 380 .egressPoint(egressPort)
377 .priority(priority) 381 .priority(priority)
382 + .constraints(CONSTRAINTS)
378 .build(); 383 .build();
379 } 384 }
380 385
...@@ -425,6 +430,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { ...@@ -425,6 +430,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
425 .ingressPoints(ingressPoints) 430 .ingressPoints(ingressPoints)
426 .egressPoint(egressPoint) 431 .egressPoint(egressPoint)
427 .priority(priority) 432 .priority(priority)
433 + .constraints(CONSTRAINTS)
428 .build(); 434 .build();
429 435
430 log.trace("Generates ConnectivityInternetToHost intent {}", intent); 436 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
...@@ -923,6 +929,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { ...@@ -923,6 +929,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
923 .ingressPoints(ingressPoints) 929 .ingressPoints(ingressPoints)
924 .egressPoint(dstConnectPoint) 930 .egressPoint(dstConnectPoint)
925 .priority(priority) 931 .priority(priority)
932 + .constraints(CONSTRAINTS)
926 .build(); 933 .build();
927 934
928 log.trace("Generates ConnectivityHostToHost = {} ", intent); 935 log.trace("Generates ConnectivityHostToHost = {} ", intent);
...@@ -950,6 +957,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { ...@@ -950,6 +957,7 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
950 .ingressPoints(ingressPoints) 957 .ingressPoints(ingressPoints)
951 .egressPoint(existingIntent.egressPoint()) 958 .egressPoint(existingIntent.egressPoint())
952 .priority(existingIntent.priority()) 959 .priority(existingIntent.priority())
960 + .constraints(CONSTRAINTS)
953 .build(); 961 .build();
954 962
955 log.trace("Update an existing MultiPointToSinglePointIntent " 963 log.trace("Update an existing MultiPointToSinglePointIntent "
......
...@@ -239,6 +239,7 @@ public class IntentSyncTest extends AbstractIntentTest { ...@@ -239,6 +239,7 @@ public class IntentSyncTest extends AbstractIntentTest {
239 .treatment(treatmentBuilder.build()) 239 .treatment(treatmentBuilder.build())
240 .ingressPoints(ingressPoints) 240 .ingressPoints(ingressPoints)
241 .egressPoint(SW1_ETH1) 241 .egressPoint(SW1_ETH1)
242 + .constraints(IntentSynchronizer.CONSTRAINTS)
242 .build(); 243 .build();
243 244
244 // Setup the expected intents 245 // Setup the expected intents
...@@ -301,6 +302,7 @@ public class IntentSyncTest extends AbstractIntentTest { ...@@ -301,6 +302,7 @@ public class IntentSyncTest extends AbstractIntentTest {
301 .treatment(treatmentBuilder.build()) 302 .treatment(treatmentBuilder.build())
302 .ingressPoints(ingressPoints) 303 .ingressPoints(ingressPoints)
303 .egressPoint(SW4_ETH1) 304 .egressPoint(SW4_ETH1)
305 + .constraints(IntentSynchronizer.CONSTRAINTS)
304 .build(); 306 .build();
305 307
306 // Setup the expected intents 308 // Setup the expected intents
...@@ -371,6 +373,7 @@ public class IntentSyncTest extends AbstractIntentTest { ...@@ -371,6 +373,7 @@ public class IntentSyncTest extends AbstractIntentTest {
371 .treatment(treatmentBuilderNew.build()) 373 .treatment(treatmentBuilderNew.build())
372 .ingressPoints(ingressPointsNew) 374 .ingressPoints(ingressPointsNew)
373 .egressPoint(SW2_ETH1) 375 .egressPoint(SW2_ETH1)
376 + .constraints(IntentSynchronizer.CONSTRAINTS)
374 .build(); 377 .build();
375 378
376 // Set up test expectation 379 // Set up test expectation
...@@ -609,6 +612,7 @@ public class IntentSyncTest extends AbstractIntentTest { ...@@ -609,6 +612,7 @@ public class IntentSyncTest extends AbstractIntentTest {
609 .treatment(treatmentBuilder.build()) 612 .treatment(treatmentBuilder.build())
610 .ingressPoints(ingressPoints) 613 .ingressPoints(ingressPoints)
611 .egressPoint(egressPoint) 614 .egressPoint(egressPoint)
615 + .constraints(IntentSynchronizer.CONSTRAINTS)
612 .build(); 616 .build();
613 return intent; 617 return intent;
614 } 618 }
......
...@@ -40,6 +40,7 @@ import org.onosproject.net.intent.Key; ...@@ -40,6 +40,7 @@ import org.onosproject.net.intent.Key;
40 import org.onosproject.net.intent.constraint.BandwidthConstraint; 40 import org.onosproject.net.intent.constraint.BandwidthConstraint;
41 import org.onosproject.net.intent.constraint.LambdaConstraint; 41 import org.onosproject.net.intent.constraint.LambdaConstraint;
42 import org.onosproject.net.intent.constraint.LinkTypeConstraint; 42 import org.onosproject.net.intent.constraint.LinkTypeConstraint;
43 +import org.onosproject.net.intent.constraint.PartialFailureConstraint;
43 import org.onosproject.net.resource.link.BandwidthResource; 44 import org.onosproject.net.resource.link.BandwidthResource;
44 import org.onlab.packet.IpPrefix; 45 import org.onlab.packet.IpPrefix;
45 import org.onlab.packet.MacAddress; 46 import org.onlab.packet.MacAddress;
...@@ -130,6 +131,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand { ...@@ -130,6 +131,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
130 required = false, multiValued = false) 131 required = false, multiValued = false)
131 private String intentKey = null; 132 private String intentKey = null;
132 133
134 + @Option(name = "--partial", description = "Allow partial installation",
135 + required = false, multiValued = false)
136 + private boolean partial = false;
137 +
133 138
134 // Treatments 139 // Treatments
135 @Option(name = "--setEthSrc", description = "Rewrite Source MAC Address", 140 @Option(name = "--setEthSrc", description = "Rewrite Source MAC Address",
...@@ -350,6 +355,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand { ...@@ -350,6 +355,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
350 } 355 }
351 constraints.add(new LinkTypeConstraint(lambda, Link.Type.OPTICAL)); 356 constraints.add(new LinkTypeConstraint(lambda, Link.Type.OPTICAL));
352 357
358 + if (partial) {
359 + constraints.add(new PartialFailureConstraint());
360 + }
361 +
353 return constraints; 362 return constraints;
354 } 363 }
355 364
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.net.intent.constraint;
17 +
18 +import org.onosproject.net.Link;
19 +import org.onosproject.net.Path;
20 +import org.onosproject.net.intent.ConnectivityIntent;
21 +import org.onosproject.net.intent.Constraint;
22 +import org.onosproject.net.intent.Intent;
23 +import org.onosproject.net.resource.link.LinkResourceService;
24 +
25 +/**
26 + * A constraint that allows intents that can only be partially compiled
27 + * (i.e. MultiPointToSinglePointIntent or SinglePointToMultiPointIntent)
28 + * to be installed when some endpoints or paths are not found.
29 + */
30 +public class PartialFailureConstraint implements Constraint {
31 + @Override
32 + public double cost(Link link, LinkResourceService resourceService) {
33 + return 1;
34 + }
35 +
36 + @Override
37 + public boolean validate(Path path, LinkResourceService resourceService) {
38 + return true;
39 + }
40 +
41 + public static boolean intentAllowsPartialFailure(Intent intent) {
42 + if (intent instanceof ConnectivityIntent) {
43 + ConnectivityIntent connectivityIntent = (ConnectivityIntent) intent;
44 + return connectivityIntent.constraints().stream()
45 + .anyMatch(c -> c instanceof PartialFailureConstraint);
46 + }
47 + return false;
48 + }
49 +}
...@@ -63,6 +63,7 @@ import static java.util.concurrent.Executors.newFixedThreadPool; ...@@ -63,6 +63,7 @@ import static java.util.concurrent.Executors.newFixedThreadPool;
63 import static java.util.concurrent.Executors.newSingleThreadExecutor; 63 import static java.util.concurrent.Executors.newSingleThreadExecutor;
64 import static org.onlab.util.Tools.groupedThreads; 64 import static org.onlab.util.Tools.groupedThreads;
65 import static org.onosproject.net.intent.IntentState.*; 65 import static org.onosproject.net.intent.IntentState.*;
66 +import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
66 import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.newInitialPhase; 67 import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.newInitialPhase;
67 import static org.onosproject.security.AppGuard.checkPermission; 68 import static org.onosproject.security.AppGuard.checkPermission;
68 import static org.slf4j.LoggerFactory.getLogger; 69 import static org.slf4j.LoggerFactory.getLogger;
...@@ -85,6 +86,8 @@ public class IntentManager ...@@ -85,6 +86,8 @@ public class IntentManager
85 86
86 private static final EnumSet<IntentState> RECOMPILE 87 private static final EnumSet<IntentState> RECOMPILE
87 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ); 88 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
89 + private static final EnumSet<IntentState> WITHDRAW
90 + = EnumSet.of(WITHDRAW_REQ, WITHDRAWING, WITHDRAWN);
88 91
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected CoreService coreService; 93 protected CoreService coreService;
...@@ -252,19 +255,18 @@ public class IntentManager ...@@ -252,19 +255,18 @@ public class IntentManager
252 submit(intent); 255 submit(intent);
253 } 256 }
254 257
255 - if (compileAllFailed) {
256 // If required, compile all currently failed intents. 258 // If required, compile all currently failed intents.
257 for (Intent intent : getIntents()) { 259 for (Intent intent : getIntents()) {
258 IntentState state = getIntentState(intent.key()); 260 IntentState state = getIntentState(intent.key());
259 - if (RECOMPILE.contains(state)) { 261 + if ((compileAllFailed && RECOMPILE.contains(state))
260 - if (state == WITHDRAW_REQ) { 262 + || intentAllowsPartialFailure(intent)) {
263 + if (WITHDRAW.contains(state)) {
261 withdraw(intent); 264 withdraw(intent);
262 } else { 265 } else {
263 submit(intent); 266 submit(intent);
264 } 267 }
265 } 268 }
266 } 269 }
267 - }
268 270
269 //FIXME 271 //FIXME
270 // for (ApplicationId appId : batches.keySet()) { 272 // for (ApplicationId appId : batches.keySet()) {
......
...@@ -276,31 +276,28 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -276,31 +276,28 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
276 delegate.triggerCompile(Collections.emptySet(), true); 276 delegate.triggerCompile(Collections.emptySet(), true);
277 277
278 } else { 278 } else {
279 - Set<Key> toBeRecompiled = new HashSet<>(); 279 + Set<Key> intentsToRecompile = new HashSet<>();
280 - boolean recompileOnly = true; 280 + boolean dontRecompileAllFailedIntents = true;
281 281
282 // Scan through the list of reasons and keep accruing all 282 // Scan through the list of reasons and keep accruing all
283 // intents that need to be recompiled. 283 // intents that need to be recompiled.
284 for (Event reason : event.reasons()) { 284 for (Event reason : event.reasons()) {
285 if (reason instanceof LinkEvent) { 285 if (reason instanceof LinkEvent) {
286 LinkEvent linkEvent = (LinkEvent) reason; 286 LinkEvent linkEvent = (LinkEvent) reason;
287 - if (linkEvent.type() == LINK_REMOVED
288 - || (linkEvent.type() == LINK_UPDATED &&
289 - linkEvent.subject().isDurable())) {
290 final LinkKey linkKey = linkKey(linkEvent.subject()); 287 final LinkKey linkKey = linkKey(linkEvent.subject());
291 synchronized (intentsByLink) { 288 synchronized (intentsByLink) {
292 Set<Key> intentKeys = intentsByLink.get(linkKey); 289 Set<Key> intentKeys = intentsByLink.get(linkKey);
293 - log.debug("recompile triggered by LinkDown {} {}", linkKey, intentKeys); 290 + log.debug("recompile triggered by LinkEvent {} ({}) for {}",
294 - toBeRecompiled.addAll(intentKeys); 291 + linkKey, linkEvent.type(), intentKeys);
292 + intentsToRecompile.addAll(intentKeys);
295 } 293 }
296 - } 294 + dontRecompileAllFailedIntents = dontRecompileAllFailedIntents &&
297 - recompileOnly = recompileOnly &&
298 (linkEvent.type() == LINK_REMOVED || 295 (linkEvent.type() == LINK_REMOVED ||
299 (linkEvent.type() == LINK_UPDATED && 296 (linkEvent.type() == LINK_UPDATED &&
300 linkEvent.subject().isDurable())); 297 linkEvent.subject().isDurable()));
301 } 298 }
302 } 299 }
303 - delegate.triggerCompile(toBeRecompiled, !recompileOnly); 300 + delegate.triggerCompile(intentsToRecompile, !dontRecompileAllFailedIntents);
304 } 301 }
305 } 302 }
306 } 303 }
......
...@@ -26,13 +26,14 @@ import org.onosproject.net.ConnectPoint; ...@@ -26,13 +26,14 @@ import org.onosproject.net.ConnectPoint;
26 import org.onosproject.net.DeviceId; 26 import org.onosproject.net.DeviceId;
27 import org.onosproject.net.Link; 27 import org.onosproject.net.Link;
28 import org.onosproject.net.Path; 28 import org.onosproject.net.Path;
29 +import org.onosproject.net.device.DeviceService;
29 import org.onosproject.net.intent.Intent; 30 import org.onosproject.net.intent.Intent;
30 import org.onosproject.net.intent.IntentCompiler; 31 import org.onosproject.net.intent.IntentCompiler;
32 +import org.onosproject.net.intent.IntentException;
31 import org.onosproject.net.intent.IntentExtensionService; 33 import org.onosproject.net.intent.IntentExtensionService;
32 import org.onosproject.net.intent.LinkCollectionIntent; 34 import org.onosproject.net.intent.LinkCollectionIntent;
33 import org.onosproject.net.intent.MultiPointToSinglePointIntent; 35 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
34 import org.onosproject.net.intent.PointToPointIntent; 36 import org.onosproject.net.intent.PointToPointIntent;
35 -import org.onosproject.net.intent.impl.PathNotFoundException;
36 import org.onosproject.net.resource.link.LinkResourceAllocations; 37 import org.onosproject.net.resource.link.LinkResourceAllocations;
37 import org.onosproject.net.topology.PathService; 38 import org.onosproject.net.topology.PathService;
38 39
...@@ -42,6 +43,9 @@ import java.util.List; ...@@ -42,6 +43,9 @@ import java.util.List;
42 import java.util.Map; 43 import java.util.Map;
43 import java.util.Set; 44 import java.util.Set;
44 45
46 +import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
47 +
48 +
45 /** 49 /**
46 * An intent compiler for 50 * An intent compiler for
47 * {@link org.onosproject.net.intent.MultiPointToSinglePointIntent}. 51 * {@link org.onosproject.net.intent.MultiPointToSinglePointIntent}.
...@@ -56,6 +60,9 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -56,6 +60,9 @@ public class MultiPointToSinglePointIntentCompiler
56 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
57 protected PathService pathService; 61 protected PathService pathService;
58 62
63 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 + protected DeviceService deviceService;
65 +
59 @Activate 66 @Activate
60 public void activate() { 67 public void activate() {
61 intentManager.registerCompiler(MultiPointToSinglePointIntent.class, this); 68 intentManager.registerCompiler(MultiPointToSinglePointIntent.class, this);
...@@ -72,11 +79,24 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -72,11 +79,24 @@ public class MultiPointToSinglePointIntentCompiler
72 Map<DeviceId, Link> links = new HashMap<>(); 79 Map<DeviceId, Link> links = new HashMap<>();
73 ConnectPoint egressPoint = intent.egressPoint(); 80 ConnectPoint egressPoint = intent.egressPoint();
74 81
82 + final boolean allowMissingPaths = intentAllowsPartialFailure(intent);
83 + boolean partialTree = false;
84 + boolean anyMissingPaths = false;
75 for (ConnectPoint ingressPoint : intent.ingressPoints()) { 85 for (ConnectPoint ingressPoint : intent.ingressPoints()) {
76 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) { 86 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
87 + if (deviceService.isAvailable(ingressPoint.deviceId())) {
88 + partialTree = true;
89 + } else {
90 + anyMissingPaths = true;
91 + }
92 +
77 continue; 93 continue;
78 } 94 }
95 +
79 Path path = getPath(ingressPoint, intent.egressPoint()); 96 Path path = getPath(ingressPoint, intent.egressPoint());
97 + if (path != null) {
98 + partialTree = true;
99 +
80 for (Link link : path.links()) { 100 for (Link link : path.links()) {
81 if (links.containsKey(link.src().deviceId())) { 101 if (links.containsKey(link.src().deviceId())) {
82 // We've already reached the existing tree with the first 102 // We've already reached the existing tree with the first
...@@ -86,9 +106,17 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -86,9 +106,17 @@ public class MultiPointToSinglePointIntentCompiler
86 links.put(link.src().deviceId(), link); 106 links.put(link.src().deviceId(), link);
87 break; 107 break;
88 } 108 }
89 -
90 links.put(link.src().deviceId(), link); 109 links.put(link.src().deviceId(), link);
91 } 110 }
111 + } else {
112 + anyMissingPaths = true;
113 + }
114 + }
115 +
116 + if (!partialTree) {
117 + throw new IntentException("Could not find any paths between ingress and egress points.");
118 + } else if (!allowMissingPaths && anyMissingPaths) {
119 + throw new IntentException("Missing some paths between ingress and egress ports.");
92 } 120 }
93 121
94 Intent result = LinkCollectionIntent.builder() 122 Intent result = LinkCollectionIntent.builder()
...@@ -111,12 +139,11 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -111,12 +139,11 @@ public class MultiPointToSinglePointIntentCompiler
111 * @param one start of the path 139 * @param one start of the path
112 * @param two end of the path 140 * @param two end of the path
113 * @return Path between the two 141 * @return Path between the two
114 - * @throws org.onosproject.net.intent.impl.PathNotFoundException if a path cannot be found
115 */ 142 */
116 private Path getPath(ConnectPoint one, ConnectPoint two) { 143 private Path getPath(ConnectPoint one, ConnectPoint two) {
117 Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId()); 144 Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
118 if (paths.isEmpty()) { 145 if (paths.isEmpty()) {
119 - throw new PathNotFoundException(one.elementId(), two.elementId()); 146 + return null;
120 } 147 }
121 // TODO: let's be more intelligent about this eventually 148 // TODO: let's be more intelligent about this eventually
122 return paths.iterator().next(); 149 return paths.iterator().next();
......
...@@ -20,8 +20,10 @@ import org.junit.Test; ...@@ -20,8 +20,10 @@ import org.junit.Test;
20 import org.onosproject.TestApplicationId; 20 import org.onosproject.TestApplicationId;
21 import org.onosproject.core.ApplicationId; 21 import org.onosproject.core.ApplicationId;
22 import org.onosproject.net.ConnectPoint; 22 import org.onosproject.net.ConnectPoint;
23 +import org.onosproject.net.DeviceId;
23 import org.onosproject.net.ElementId; 24 import org.onosproject.net.ElementId;
24 import org.onosproject.net.Path; 25 import org.onosproject.net.Path;
26 +import org.onosproject.net.device.DeviceServiceAdapter;
25 import org.onosproject.net.flow.TrafficSelector; 27 import org.onosproject.net.flow.TrafficSelector;
26 import org.onosproject.net.flow.TrafficTreatment; 28 import org.onosproject.net.flow.TrafficTreatment;
27 import org.onosproject.net.intent.AbstractIntentTest; 29 import org.onosproject.net.intent.AbstractIntentTest;
...@@ -92,6 +94,16 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes ...@@ -92,6 +94,16 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes
92 } 94 }
93 95
94 /** 96 /**
97 + * Mocks the device service so that a device appears available in the test.
98 + */
99 + private static class MockDeviceService extends DeviceServiceAdapter {
100 + @Override
101 + public boolean isAvailable(DeviceId deviceId) {
102 + return true;
103 + }
104 + }
105 +
106 + /**
95 * Creates a MultiPointToSinglePoint intent for a group of ingress points 107 * Creates a MultiPointToSinglePoint intent for a group of ingress points
96 * and an egress point. 108 * and an egress point.
97 * 109 *
...@@ -126,6 +138,7 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes ...@@ -126,6 +138,7 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes
126 MultiPointToSinglePointIntentCompiler compiler = 138 MultiPointToSinglePointIntentCompiler compiler =
127 new MultiPointToSinglePointIntentCompiler(); 139 new MultiPointToSinglePointIntentCompiler();
128 compiler.pathService = new MockPathService(hops); 140 compiler.pathService = new MockPathService(hops);
141 + compiler.deviceService = new MockDeviceService();
129 return compiler; 142 return compiler;
130 } 143 }
131 144
......
...@@ -157,6 +157,7 @@ import org.onosproject.net.intent.constraint.LambdaConstraint; ...@@ -157,6 +157,7 @@ import org.onosproject.net.intent.constraint.LambdaConstraint;
157 import org.onosproject.net.intent.constraint.LatencyConstraint; 157 import org.onosproject.net.intent.constraint.LatencyConstraint;
158 import org.onosproject.net.intent.constraint.LinkTypeConstraint; 158 import org.onosproject.net.intent.constraint.LinkTypeConstraint;
159 import org.onosproject.net.intent.constraint.ObstacleConstraint; 159 import org.onosproject.net.intent.constraint.ObstacleConstraint;
160 +import org.onosproject.net.intent.constraint.PartialFailureConstraint;
160 import org.onosproject.net.intent.constraint.WaypointConstraint; 161 import org.onosproject.net.intent.constraint.WaypointConstraint;
161 import org.onosproject.net.link.DefaultLinkDescription; 162 import org.onosproject.net.link.DefaultLinkDescription;
162 import org.onosproject.net.newresource.DefaultResource; 163 import org.onosproject.net.newresource.DefaultResource;
...@@ -412,6 +413,7 @@ public final class KryoNamespaces { ...@@ -412,6 +413,7 @@ public final class KryoNamespaces {
412 ObstacleConstraint.class, 413 ObstacleConstraint.class,
413 AnnotationConstraint.class, 414 AnnotationConstraint.class,
414 BooleanConstraint.class, 415 BooleanConstraint.class,
416 + PartialFailureConstraint.class,
415 IntentOperation.class, 417 IntentOperation.class,
416 FlowRuleExtPayLoad.class, 418 FlowRuleExtPayLoad.class,
417 Frequency.class, 419 Frequency.class,
......