Add MPLS encapsulation behaviour ONOS-3467

- MPLS encapsulation using constraint
- New MPLS encapsulation test
- Fix VLAN encapsulation test

Change-Id: I94670bcd51a95a0272f786681e51d6785a56c4f5
diff --git a/cli/src/main/java/org/onosproject/cli/net/AddMplsIntent.java b/cli/src/main/java/org/onosproject/cli/net/AddMplsIntent.java
index a736f99..07b5ed0 100644
--- a/cli/src/main/java/org/onosproject/cli/net/AddMplsIntent.java
+++ b/cli/src/main/java/org/onosproject/cli/net/AddMplsIntent.java
@@ -31,7 +31,11 @@
 
 /**
  * Installs MPLS intents.
+ *
+ * @deprecated in Goldeneye Release, in favour of encapsulation
+ * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
  */
+@Deprecated
 @Command(scope = "onos", name = "add-mpls-intent", description = "Installs mpls connectivity intent")
 public class AddMplsIntent extends ConnectivityIntentCommand {
 
diff --git a/core/api/src/main/java/org/onosproject/net/intent/MplsIntent.java b/core/api/src/main/java/org/onosproject/net/intent/MplsIntent.java
index 7df3c81..f5c1c43 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/MplsIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/MplsIntent.java
@@ -34,8 +34,14 @@
 
 /**
  * Abstraction of MPLS label-switched connectivity.
+ *
+ * @deprecated in Goldeneye Release, in favour of encapsulation
+ * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
+ * with Encasulation type {@link org.onosproject.net.EncapsulationType} MPLS.
+ *
  */
 @Beta
+@Deprecated
 public final class MplsIntent extends ConnectivityIntent {
 
     private final ConnectPoint ingressPoint;
diff --git a/core/api/src/main/java/org/onosproject/net/intent/MplsPathIntent.java b/core/api/src/main/java/org/onosproject/net/intent/MplsPathIntent.java
index 4548c44..9cb24d6 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/MplsPathIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/MplsPathIntent.java
@@ -30,8 +30,12 @@
 
 /**
  * Abstraction of explicit MPLS label-switched path.
+ *
+ * @deprecated in Goldeneye Release, in favour of encapsulation
+ * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
  */
 @Beta
+@Deprecated
 public final class MplsPathIntent extends PathIntent {
 
     private final Optional<MplsLabel> ingressLabel;
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java
index 52621e2..f3c5aa9 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java
@@ -36,7 +36,11 @@
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.resource.link.LinkResourceAllocations;
 
-
+/**
+ * @deprecated in Goldeneye Release, in favour of encapsulation
+ * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
+ */
+@Deprecated
 @Component(immediate = true)
 public class MplsIntentCompiler  extends ConnectivityIntentCompiler<MplsIntent> {
 
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
index 28b8a04..095c8e9 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
@@ -68,6 +68,11 @@
 import static org.onosproject.net.LinkKey.linkKey;
 import static org.slf4j.LoggerFactory.getLogger;
 
+/**
+ * @deprecated in Goldeneye Release, in favour of encapsulation
+ * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
+ */
+@Deprecated
 @Component(immediate = true)
 public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
 
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
index 7514250..1cffdd4 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
@@ -25,6 +25,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -35,12 +38,16 @@
 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.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.intent.PathIntent;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.intent.impl.IntentCompilationException;
 import org.onosproject.net.newresource.Resource;
+import org.onosproject.net.newresource.ResourceAllocation;
 import org.onosproject.net.newresource.ResourceService;
 import org.onosproject.net.newresource.Resources;
 import org.slf4j.Logger;
@@ -227,6 +234,185 @@
         }
     }
 
+    private Map<LinkKey, MplsLabel> assignMplsLabel(PathCompilerCreateFlow creator, PathIntent intent) {
+                Set<LinkKey> linkRequest =
+                Sets.newHashSetWithExpectedSize(intent.path()
+                        .links().size() - 2);
+        for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+            LinkKey link = linkKey(intent.path().links().get(i));
+            linkRequest.add(link);
+            // add the inverse link. I want that the VLANID is reserved both for
+            // the direct and inverse link
+            linkRequest.add(linkKey(link.dst(), link.src()));
+        }
+
+        Map<LinkKey, MplsLabel> labels = findMplsLabels(creator, linkRequest);
+        if (labels.isEmpty()) {
+            throw new IntentCompilationException("No available MPLS Label");
+        }
+
+        // for short term solution: same label is used for both directions
+        // TODO: introduce the concept of Tx and Rx resources of a port
+        Set<Resource> resources = labels.entrySet().stream()
+                .flatMap(x -> Stream.of(
+                        Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
+                                .resource(),
+                        Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
+                                .resource()
+                ))
+                .collect(Collectors.toSet());
+        List<ResourceAllocation> allocations =
+                creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
+        if (allocations.isEmpty()) {
+            Collections.emptyMap();
+        }
+
+        return labels;
+    }
+
+    private Map<LinkKey, MplsLabel> findMplsLabels(PathCompilerCreateFlow creator, Set<LinkKey> links) {
+        Map<LinkKey, MplsLabel> labels = new HashMap<>();
+        for (LinkKey link : links) {
+            Set<MplsLabel> forward = findMplsLabel(creator, link.src());
+            Set<MplsLabel> backward = findMplsLabel(creator, link.dst());
+            Set<MplsLabel> common = Sets.intersection(forward, backward);
+            if (common.isEmpty()) {
+                continue;
+            }
+            labels.put(link, common.iterator().next());
+        }
+
+        return labels;
+    }
+
+    private Set<MplsLabel> findMplsLabel(PathCompilerCreateFlow creator, ConnectPoint cp) {
+        return creator.resourceService().getAvailableResourceValues(
+                Resources.discrete(cp.deviceId(), cp.port()).id(),
+                MplsLabel.class);
+    }
+
+    private void manageMplsEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
+                                           List<DeviceId> devices,
+                                           PathIntent intent) {
+        Map<LinkKey, MplsLabel> mplsLabels = assignMplsLabel(creator, intent);
+
+        Iterator<Link> links = intent.path().links().iterator();
+        Link srcLink = links.next();
+
+        Link link = links.next();
+        // List of flow rules to be installed
+
+        // Ingress traffic
+        MplsLabel mplsLabel = mplsLabels.get(linkKey(link));
+        if (mplsLabel == null) {
+            throw new IntentCompilationException("No available MPLS Label for " + link);
+        }
+        MplsLabel prevMplsLabel = mplsLabel;
+
+        Optional<MplsCriterion> mplsCriterion = intent.selector().criteria()
+                .stream().filter(criterion -> criterion.type() == Criterion.Type.MPLS_LABEL)
+                .map(criterion -> (MplsCriterion) criterion)
+                .findAny();
+
+        //Push MPLS if selector does not include MPLS
+        TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
+        if (!mplsCriterion.isPresent()) {
+            treatBuilder.pushMpls();
+        }
+        //Tag the traffic with the new encapsulation MPLS label
+        treatBuilder.setMpls(mplsLabel);
+        creator.createFlow(intent.selector(), treatBuilder.build(),
+                srcLink.dst(), link.src(), intent.priority(), true, flows, devices);
+
+        ConnectPoint prev = link.dst();
+
+        while (links.hasNext()) {
+
+            link = links.next();
+
+            if (links.hasNext()) {
+                // Transit traffic
+                MplsLabel transitMplsLabel = mplsLabels.get(linkKey(link));
+                if (transitMplsLabel == null) {
+                    throw new IntentCompilationException("No available MPLS label for " + link);
+                }
+                prevMplsLabel = transitMplsLabel;
+
+                TrafficSelector transitSelector = DefaultTrafficSelector.builder()
+                        .matchInPort(prev.port())
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .matchMplsLabel(prevMplsLabel).build();
+
+                TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
+
+                // Set the new MPLS Label only if the previous one is different
+                if (!prevMplsLabel.equals(transitMplsLabel)) {
+                    transitTreat.setMpls(transitMplsLabel);
+                }
+                creator.createFlow(transitSelector,
+                        transitTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
+                prev = link.dst();
+            } else {
+                TrafficSelector.Builder egressSelector = DefaultTrafficSelector.builder()
+                        .matchInPort(prev.port())
+                        .matchEthType(Ethernet.MPLS_UNICAST)
+                        .matchMplsLabel(prevMplsLabel);
+                TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
+
+                // Egress traffic
+                // check if the treatement is popVlan or setVlan (rewrite),
+                // than selector needs to match any VlanId
+                for (Instruction instruct : intent.treatment().allInstructions()) {
+                    if (instruct instanceof L2ModificationInstruction) {
+                        L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
+                        if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+                            break;
+                        }
+                        if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
+                                l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+                            egressSelector.matchVlanId(VlanId.ANY);
+                        }
+                    }
+                }
+
+                if (mplsCriterion.isPresent()) {
+                    egressTreat.setMpls(mplsCriterion.get().label());
+                } else {
+                    egressTreat.popMpls(outputEthType(intent.selector()));
+                }
+
+
+                if (mplsCriterion.isPresent()) {
+                    egressTreat.setMpls(mplsCriterion.get().label());
+                } else {
+                    egressTreat.popVlan();
+                }
+
+                creator.createFlow(egressSelector.build(),
+                        egressTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
+            }
+
+        }
+
+    }
+
+    private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) {
+        return labels.get(link);
+    }
+
+    // if the ingress ethertype is defined, the egress traffic
+    // will be use that value, otherwise the IPv4 ethertype is used.
+    private EthType outputEthType(TrafficSelector selector) {
+        Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
+        if (c != null && c instanceof EthTypeCriterion) {
+            EthTypeCriterion ethertype = (EthTypeCriterion) c;
+            return ethertype.ethType();
+        } else {
+            return EthType.EtherType.IPV4.ethType();
+        }
+    }
+
+
     /**
      * Compiles an intent down to flows.
      *
@@ -263,7 +449,10 @@
                     switch (type) {
                         case VLAN:
                             manageVlanEncap(creator, flows, devices, intent);
-                            // TODO: implement MPLS case here
+                            break;
+                        case MPLS:
+                             manageMplsEncap(creator, flows, devices, intent);
+                            break;
                         default:
                             // Nothing to do
                     }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
index 039a1f5..7222f0f 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -15,10 +15,7 @@
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
+import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -42,10 +39,13 @@
 import org.onosproject.net.resource.link.LinkResourceAllocations;
 import org.slf4j.Logger;
 
-import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+
 @Component(immediate = true)
 public class PathIntentCompiler
         extends PathCompiler<FlowRule>
@@ -84,6 +84,7 @@
         List<DeviceId> devices = new LinkedList<>();
         compile(this, intent, rules, devices);
 
+
         return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
     }
 
@@ -97,12 +98,14 @@
         return resourceService;
     }
 
+
     @Override
     public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
                            ConnectPoint ingress, ConnectPoint egress,
                            int priority, boolean applyTreatment,
                            List<FlowRule> rules,
                            List<DeviceId> devices) {
+
         TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
                 .matchInPort(ingress.port())
                 .build();
@@ -123,5 +126,6 @@
                 .fromApp(appId)
                 .makePermanent()
                 .build());
+
     }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
index 17b173a..2716b5a 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
@@ -19,6 +19,8 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.cfg.ComponentConfigAdapter;
@@ -28,6 +30,7 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultLink;
 import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Link;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -52,14 +55,18 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.number.OrderingComparison.greaterThan;
 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
 import static org.onosproject.net.Link.Type.DIRECT;
-import static org.onosproject.net.NetTestTools.*;
+import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.NetTestTools.PID;
+import static org.onosproject.net.NetTestTools.connectPoint;
 
 /**
  * Unit tests for PathIntentCompiler.
@@ -99,8 +106,9 @@
     );
     private final int hops = links.size() - 1;
     private PathIntent intent;
-    private PathIntent constraintIntent;
+    private PathIntent constraintVlanIntent;
     private PathIntent constrainIngressEgressVlanIntent;
+    private PathIntent constraintMplsIntent;
 
     /**
      * Configures objects used in all the test cases.
@@ -123,8 +131,9 @@
                 .priority(PRIORITY)
                 .path(new DefaultPath(pid, links, hops))
                 .build();
+
         //Intent with VLAN encap without egress VLAN
-        constraintIntent = PathIntent.builder()
+        constraintVlanIntent = PathIntent.builder()
                 .appId(APP_ID)
                 .selector(selector)
                 .treatment(treatment)
@@ -132,6 +141,7 @@
                 .constraints(ImmutableList.of(new EncapsulationConstraint(EncapsulationType.VLAN)))
                 .path(new DefaultPath(pid, links, hops))
                 .build();
+
         //Intent with VLAN encap with ingress and egress VLAN
         constrainIngressEgressVlanIntent = PathIntent.builder()
                 .appId(APP_ID)
@@ -141,6 +151,15 @@
                 .constraints(ImmutableList.of(new EncapsulationConstraint(EncapsulationType.VLAN)))
                 .path(new DefaultPath(pid, links, hops))
                 .build();
+
+        constraintMplsIntent = PathIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .priority(PRIORITY)
+                .constraints(ImmutableList.of(new EncapsulationConstraint(EncapsulationType.MPLS)))
+                .path(new DefaultPath(pid, links, hops))
+                .build();
         intentExtensionService = createMock(IntentExtensionService.class);
         intentExtensionService.registerCompiler(PathIntent.class, sut);
         intentExtensionService.unregisterCompiler(PathIntent.class);
@@ -179,34 +198,32 @@
                 .filter(x -> x.deviceId().equals(d1p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule1.deviceId(), is(d1p0.deviceId()));
+        verifyIdAndPriority(rule1, d1p0.deviceId());
         assertThat(rule1.selector(),
                 is(DefaultTrafficSelector.builder(selector).matchInPort(d1p0.port()).build()));
         assertThat(rule1.treatment(),
                 is(DefaultTrafficTreatment.builder().setOutput(d1p1.port()).build()));
-        assertThat(rule1.priority(), is(intent.priority()));
+
 
         FlowRule rule2 = rules.stream()
                 .filter(x -> x.deviceId().equals(d2p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule2.deviceId(), is(d2p0.deviceId()));
+        verifyIdAndPriority(rule2, d2p0.deviceId());
         assertThat(rule2.selector(),
                 is(DefaultTrafficSelector.builder(selector).matchInPort(d2p0.port()).build()));
         assertThat(rule2.treatment(),
                 is(DefaultTrafficTreatment.builder().setOutput(d2p1.port()).build()));
-        assertThat(rule2.priority(), is(intent.priority()));
 
         FlowRule rule3 = rules.stream()
                 .filter(x -> x.deviceId().equals(d3p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule3.deviceId(), is(d3p1.deviceId()));
+        verifyIdAndPriority(rule3, d3p1.deviceId());
         assertThat(rule3.selector(),
                 is(DefaultTrafficSelector.builder(selector).matchInPort(d3p1.port()).build()));
         assertThat(rule3.treatment(),
                 is(DefaultTrafficTreatment.builder(treatment).setOutput(d3p0.port()).build()));
-        assertThat(rule3.priority(), is(intent.priority()));
 
         sut.deactivate();
     }
@@ -216,10 +233,10 @@
      * VLAN {@link EncapsulationType} encapsulation constraint {@link EncapsulationConstraint}.
      */
     @Test
-    public void testEncapCompile() {
+    public void testVlanEncapCompile() {
         sut.activate();
 
-        List<Intent> compiled = sut.compile(constraintIntent, Collections.emptyList(), Collections.emptySet());
+        List<Intent> compiled = sut.compile(constraintVlanIntent, Collections.emptyList(), Collections.emptySet());
         assertThat(compiled, hasSize(1));
 
         Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
@@ -229,28 +246,26 @@
                 .filter(x -> x.deviceId().equals(d1p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule1.deviceId(), is(d1p0.deviceId()));
-        assertThat(rule1.priority(), is(intent.priority()));
-        verifyEncapSelector(rule1.selector(), d1p0, VlanId.NONE);
-        VlanId vlanToEncap = verifyEncapTreatment(rule1.treatment(), d1p1, true, false);
+        verifyIdAndPriority(rule1, d1p0.deviceId());
+        assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector)
+                                        .matchInPort(d1p0.port()).build()));
+        VlanId vlanToEncap = verifyVlanEncapTreatment(rule1.treatment(), d1p1, true, false);
 
         FlowRule rule2 = rules.stream()
                 .filter(x -> x.deviceId().equals(d2p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule2.deviceId(), is(d2p0.deviceId()));
-        assertThat(rule2.priority(), is(intent.priority()));
-        verifyEncapSelector(rule2.selector(), d2p0, vlanToEncap);
-        verifyEncapTreatment(rule2.treatment(), d2p1, false, false);
+        verifyIdAndPriority(rule2, d2p0.deviceId());
+        verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap);
+        verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false);
 
         FlowRule rule3 = rules.stream()
                 .filter(x -> x.deviceId().equals(d3p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule3.deviceId(), is(d3p1.deviceId()));
-        assertThat(rule3.priority(), is(intent.priority()));
-        verifyEncapSelector(rule3.selector(), d3p1, vlanToEncap);
-        verifyEncapTreatment(rule3.treatment(), d3p0, false, true);
+        verifyIdAndPriority(rule3, d3p1.deviceId());
+        verifyVlanEncapSelector(rule3.selector(), d3p1, vlanToEncap);
+        verifyVlanEncapTreatment(rule3.treatment(), d3p0, false, true);
 
         sut.deactivate();
     }
@@ -275,27 +290,24 @@
                 .filter(x -> x.deviceId().equals(d1p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule1.deviceId(), is(d1p0.deviceId()));
-        assertThat(rule1.priority(), is(intent.priority()));
-        verifyEncapSelector(rule1.selector(), d1p0, ingressVlan);
-        VlanId vlanToEncap = verifyEncapTreatment(rule1.treatment(), d1p1, true, false);
+        verifyIdAndPriority(rule1, d1p0.deviceId());
+        verifyVlanEncapSelector(rule1.selector(), d1p0, ingressVlan);
+        VlanId vlanToEncap = verifyVlanEncapTreatment(rule1.treatment(), d1p1, true, false);
 
         FlowRule rule2 = rules.stream()
                 .filter(x -> x.deviceId().equals(d2p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule2.deviceId(), is(d2p0.deviceId()));
-        assertThat(rule2.priority(), is(intent.priority()));
-        verifyEncapSelector(rule2.selector(), d2p0, vlanToEncap);
-        verifyEncapTreatment(rule2.treatment(), d2p1, false, false);
+        verifyIdAndPriority(rule2, d2p0.deviceId());
+        verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap);
+        verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false);
 
         FlowRule rule3 = rules.stream()
                 .filter(x -> x.deviceId().equals(d3p0.deviceId()))
                 .findFirst()
                 .get();
-        assertThat(rule3.deviceId(), is(d3p1.deviceId()));
-        assertThat(rule3.priority(), is(intent.priority()));
-        verifyEncapSelector(rule3.selector(), d3p1, vlanToEncap);
+        verifyIdAndPriority(rule3, d3p1.deviceId());
+        verifyVlanEncapSelector(rule3.selector(), d3p1, vlanToEncap);
         Set<L2ModificationInstruction.ModVlanIdInstruction> vlanMod = rule3.treatment().allInstructions().stream()
                 .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction)
                 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x)
@@ -311,7 +323,7 @@
         sut.deactivate();
     }
 
-    private VlanId verifyEncapTreatment(TrafficTreatment trafficTreatment,
+    private VlanId verifyVlanEncapTreatment(TrafficTreatment trafficTreatment,
                                         ConnectPoint egress, boolean isIngress, boolean isEgress) {
         Set<Instructions.OutputInstruction> ruleOutput = trafficTreatment.allInstructions().stream()
                 .filter(treat -> treat instanceof Instructions.OutputInstruction)
@@ -347,9 +359,101 @@
 
     }
 
-    private void verifyEncapSelector(TrafficSelector trafficSelector, ConnectPoint ingress, VlanId vlanToMatch) {
+    private void verifyVlanEncapSelector(TrafficSelector trafficSelector, ConnectPoint ingress, VlanId vlanToMatch) {
 
-        is(DefaultTrafficSelector.builder(selector).matchInPort(ingress.port())
-                   .matchVlanId(vlanToMatch).build());
+        assertThat(trafficSelector, is(DefaultTrafficSelector.builder().matchInPort(ingress.port())
+                   .matchVlanId(vlanToMatch).build()));
+    }
+
+    /**
+     * Tests the compilation behavior of the path intent compiler in case of
+     * encasulation costraint {@link EncapsulationConstraint}.
+     */
+    @Test
+    public void testMplsEncapCompile() {
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(constraintMplsIntent, Collections.emptyList(), Collections.emptySet());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        FlowRule rule1 = rules.stream()
+                .filter(x -> x.deviceId().equals(d1p0.deviceId()))
+                .findFirst()
+                .get();
+        verifyIdAndPriority(rule1, d1p0.deviceId());
+        assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector)
+                                        .matchInPort(d1p0.port()).build()));
+        MplsLabel mplsLabelToEncap = verifyMplsEncapTreatment(rule1.treatment(), d1p1, true, false);
+
+        FlowRule rule2 = rules.stream()
+                .filter(x -> x.deviceId().equals(d2p0.deviceId()))
+                .findFirst()
+                .get();
+        verifyIdAndPriority(rule2, d2p0.deviceId());
+        verifyMplsEncapSelector(rule2.selector(), d2p0, mplsLabelToEncap);
+        verifyMplsEncapTreatment(rule2.treatment(), d2p1, false, false);
+
+        FlowRule rule3 = rules.stream()
+                .filter(x -> x.deviceId().equals(d3p0.deviceId()))
+                .findFirst()
+                .get();
+        verifyIdAndPriority(rule3, d3p1.deviceId());
+        verifyMplsEncapSelector(rule3.selector(), d3p1, mplsLabelToEncap);
+        verifyMplsEncapTreatment(rule3.treatment(), d3p0, false, true);
+
+        sut.deactivate();
+    }
+
+
+    private MplsLabel verifyMplsEncapTreatment(TrafficTreatment trafficTreatment,
+                                               ConnectPoint egress, boolean isIngress, boolean isEgress) {
+        Set<Instructions.OutputInstruction> ruleOutput = trafficTreatment.allInstructions().stream()
+                .filter(treat -> treat instanceof Instructions.OutputInstruction)
+                .map(treat -> (Instructions.OutputInstruction) treat)
+                .collect(Collectors.toSet());
+        assertThat(ruleOutput, hasSize(1));
+        assertThat((ruleOutput.iterator().next()).port(), is(egress.port()));
+        MplsLabel mplsToEncap = MplsLabel.mplsLabel(0);
+        if (isIngress && !isEgress) {
+            Set<L2ModificationInstruction.ModMplsLabelInstruction> mplsRules =
+                    trafficTreatment.allInstructions().stream()
+                            .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
+                            .map(x -> (L2ModificationInstruction.ModMplsLabelInstruction) x)
+                            .collect(Collectors.toSet());
+            assertThat(mplsRules, hasSize(1));
+            L2ModificationInstruction.ModMplsLabelInstruction mplsRule = mplsRules.iterator().next();
+            assertThat(mplsRule.mplsLabel().toInt(), greaterThan(0));
+            mplsToEncap = mplsRule.mplsLabel();
+        } else if (!isIngress && !isEgress) {
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
+                               .collect(Collectors.toSet()), hasSize(0));
+        } else {
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
+                               .collect(Collectors.toSet()), hasSize(0));
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.PushHeaderInstructions)
+                               .collect(Collectors.toSet()), hasSize(1));
+
+        }
+
+        return mplsToEncap;
+
+    }
+
+    private void verifyMplsEncapSelector(TrafficSelector trafficSelector, ConnectPoint ingress, MplsLabel mplsLabel) {
+
+        assertThat(trafficSelector, is(DefaultTrafficSelector.builder()
+                                               .matchInPort(ingress.port()).matchEthType(Ethernet.MPLS_UNICAST)
+                                               .matchMplsLabel(mplsLabel).build()));
+    }
+
+    private void verifyIdAndPriority(FlowRule rule, DeviceId deviceId) {
+        assertThat(rule.deviceId(), is(deviceId));
+        assertThat(rule.priority(), is(PRIORITY));
     }
 }