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

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 e7e6a86..b70cb14 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();
     }
 
@@ -427,6 +432,7 @@
                 .ingressPoints(ingressPoints)
                 .egressPoint(egressPoint)
                 .priority(priority)
+                .constraints(CONSTRAINTS)
                 .build();
 
         log.trace("Generates ConnectivityInternetToHost intent {}", intent);
@@ -925,6 +931,7 @@
                 .ingressPoints(ingressPoints)
                 .egressPoint(dstConnectPoint)
                 .priority(priority)
+                .constraints(CONSTRAINTS)
                 .build();
 
         log.trace("Generates ConnectivityHostToHost = {} ", intent);
@@ -952,6 +959,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 8bc0bc4..1389ffa 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -39,6 +39,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;
@@ -125,6 +126,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",
@@ -318,6 +323,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 16404e3..3cdf2ca 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
@@ -25,8 +25,8 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.core.IdGenerator;
 import org.onosproject.core.Permission;
-import org.onosproject.event.ListenerRegistry;
 import org.onosproject.event.EventDeliveryService;
+import org.onosproject.event.ListenerRegistry;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.flow.FlowRuleOperationsContext;
@@ -64,9 +64,10 @@
 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.slf4j.LoggerFactory.getLogger;
 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);
 
     private final ListenerRegistry<IntentEvent, IntentListener>
             listenerRegistry = new ListenerRegistry<>();
@@ -282,16 +285,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 3a40622..8be578f 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
@@ -15,12 +15,8 @@
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -30,20 +26,25 @@
 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;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
+import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
 
 /**
  * An intent compiler for
@@ -59,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);
@@ -76,27 +80,46 @@
         Map<DeviceId, Link> edgeLinks = 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())) {
-                edgeLinks.put(ingressPoint.deviceId(), createEdgeLink(ingressPoint, true));
-                edgeLinks.put(egressPoint.deviceId(), createEdgeLink(egressPoint, false));
+                if (deviceService.isAvailable(ingressPoint.deviceId())) {
+                    partialTree = true;
+                    edgeLinks.put(ingressPoint.deviceId(), createEdgeLink(ingressPoint, true));
+                    edgeLinks.put(egressPoint.deviceId(), createEdgeLink(egressPoint, false));
+                } else {
+                    anyMissingPaths = true;
+                }
             } else {
                 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 (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);
+                        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.");
+        }
+
         Set<Link> allLinks = Sets.newHashSet(links.values());
         allLinks.addAll(edgeLinks.values());
         Intent result = LinkCollectionIntent.builder()
@@ -119,12 +142,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 a3b4525..d481aaf 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
@@ -15,17 +15,15 @@
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 import org.hamcrest.Matchers;
 import org.junit.Test;
 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;
@@ -36,6 +34,10 @@
 import org.onosproject.net.topology.LinkWeight;
 import org.onosproject.net.topology.PathService;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -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 f930f8c..ffc6a0f 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
@@ -151,6 +151,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.packet.DefaultOutboundPacket;
@@ -389,6 +390,7 @@
                     ObstacleConstraint.class,
                     AnnotationConstraint.class,
                     BooleanConstraint.class,
+                    PartialFailureConstraint.class,
                     IntentOperation.class,
                     FlowRuleExtPayLoad.class,
                     Frequency.class,