Extension of the LinkCollectionCompiler to support Domains

Introduced the DomainConstraint which marks intents that allow domains.
Added the handling of domains to the base compiler and its implementations.
Fixed existing test by adding the domain service and added new ones for domains.

Change-Id: I6594e92e41c2434a9b667415e6fb90a6c571df79
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 7ebc3be..0fd65d0 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -37,6 +37,7 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.DomainConstraint;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
 import org.onosproject.net.intent.constraint.PartialFailureConstraint;
@@ -182,6 +183,10 @@
             required = false, multiValued = false)
     private boolean hashedPathSelection = false;
 
+    @Option(name = "--domains", description = "Allow domain delegation",
+            required = false, multiValued = false)
+    private boolean domains = false;
+
     // Resource Group
     @Option(name = "-r", aliases = "--resourceGroup", description = "Resource Group Id",
             required = false, multiValued = false)
@@ -400,6 +405,11 @@
         if (hashedPathSelection) {
             constraints.add(new HashedPathSelectionConstraint());
         }
+
+        // Check for domain processing
+        if (domains) {
+            constraints.add(DomainConstraint.domain());
+        }
         return constraints;
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/intent/constraint/DomainConstraint.java b/core/api/src/main/java/org/onosproject/net/intent/constraint/DomainConstraint.java
new file mode 100644
index 0000000..42fb6db
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/constraint/DomainConstraint.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present 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 com.google.common.annotations.Beta;
+
+/**
+ * This constraint is a flag and tells the compiler that it is allowed to generate
+ * {@link org.onosproject.net.domain.DomainIntent}.
+ */
+@Beta
+public class DomainConstraint extends MarkerConstraint {
+
+    private static final DomainConstraint DOMAIN_CONSTRAINT =
+            new DomainConstraint();
+
+    protected DomainConstraint() {
+    }
+
+    /**
+     * Returns domain constraint.
+     *
+     * @return domain constraint
+     */
+    public static DomainConstraint domain() {
+        return DOMAIN_CONSTRAINT;
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
index 7d5c4db..e65d256 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.net.intent.impl.compiler;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
@@ -33,6 +34,9 @@
 import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.domain.DomainId;
+import org.onosproject.net.domain.DomainPointToPointIntent;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -61,10 +65,14 @@
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
+import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompilationException;
 import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.constraint.DomainConstraint;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.resource.impl.LabelAllocator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.List;
 import java.util.Map;
@@ -72,6 +80,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.onosproject.net.domain.DomainId.LOCAL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.*;
 
 /**
@@ -187,6 +196,8 @@
      */
     private static final String UNSUPPORTED_INSTRUCTION = "Unknown instruction type";
 
+    private static Logger log = LoggerFactory.getLogger(LinkCollectionCompiler.class);
+
     /**
      * Influence compiler behavior.
      *
@@ -955,6 +966,138 @@
                 .map(x -> (EncapsulationConstraint) x).findAny();
     }
 
+    /**
+     * Checks if domain processing is enabled for this intent by looking for the {@link DomainConstraint}.
+     *
+     * @param intent the intent to be checked
+     * @return is the processing of domains enabled
+     */
+    protected boolean isDomainProcessingEnabled(LinkCollectionIntent intent) {
+        return intent.constraints().contains(DomainConstraint.domain());
+    }
+
+    /**
+     * Creates the domain intents that the {@link LinkCollectionIntent} contains.
+     *
+     * @param intent        the link collection intent
+     * @param domainService the domain service
+     * @return the resulting list of domain intents
+     */
+    protected List<Intent> getDomainIntents(LinkCollectionIntent intent,
+                                            DomainService domainService) {
+        ImmutableList.Builder<Intent> intentList = ImmutableList.builder();
+        // domain handling is only applied for a single entry and exit point
+        // TODO: support multi point to multi point
+        if (intent.filteredIngressPoints().size() != 1 || intent
+                .filteredEgressPoints().size() != 1) {
+            log.warn("Multiple ingress or egress ports not supported!");
+            return intentList.build();
+        }
+        ImmutableList.Builder<Link> domainLinks = ImmutableList.builder();
+        // get the initial ingress connection point
+        FilteredConnectPoint ingress =
+                intent.filteredIngressPoints().iterator().next();
+        FilteredConnectPoint egress;
+        DeviceId currentDevice = ingress.connectPoint().deviceId();
+        // the current domain (or LOCAL)
+        DomainId currentDomain = domainService.getDomain(currentDevice);
+        // if we entered a domain store the domain ingress
+        FilteredConnectPoint domainIngress =
+                LOCAL.equals(currentDomain) ? null : ingress;
+        // loop until (hopefully) all links have been checked once
+        // this is necessary because a set is not sorted by default
+        for (int i = 0; i < intent.links().size(); i++) {
+            // find the next link
+            List<Link> nextLinks =
+                    getEgressLinks(intent.links(), currentDevice);
+            // no matching link exists
+            if (nextLinks.isEmpty()) {
+                throw new IntentCompilationException(
+                        "No matching link starting at " + ingress
+                                .connectPoint().deviceId());
+            }
+            // get the first link
+            Link nextLink = nextLinks.get(0);
+            ingress = new FilteredConnectPoint(nextLink.src());
+            egress = new FilteredConnectPoint(nextLink.dst());
+            // query the domain for the domain of the link's destination
+            DomainId dstDomain = domainService
+                    .getDomain(egress.connectPoint().deviceId());
+            if (!currentDomain.equals(dstDomain)) {
+                // we are leaving the current domain or LOCAL
+                log.debug("Domain transition from {} to {}.", currentDomain,
+                          dstDomain);
+                if (!LOCAL.equals(currentDomain)) {
+                    // add the domain intent to the intent list
+                    intentList.add(createDomainP2PIntent(intent, domainIngress,
+                                                         ingress,
+                                                         domainLinks.build()));
+                    // TODO: might end up with an unused builder
+                    // reset domain links builder
+                    domainLinks = ImmutableList.builder();
+                }
+                // update current domain (might be LOCAL)
+                currentDomain = dstDomain;
+                // update the domain's ingress
+                domainIngress = LOCAL.equals(currentDomain) ? null : egress;
+            } else {
+                if (!LOCAL.equals(currentDomain)) {
+                    // we are staying in the same domain, store the traversed link
+                    domainLinks.add(nextLink);
+                    log.debug("{} belongs to the same domain.",
+                              egress.connectPoint().deviceId());
+                }
+            }
+            currentDevice = egress.connectPoint().deviceId();
+        }
+        // get the egress point
+        egress = intent.filteredEgressPoints().iterator().next();
+        // still inside a domain?
+        if (!LOCAL.equals(currentDomain) &&
+                currentDomain.equals(domainService.getDomain(
+                        egress.connectPoint().deviceId()))) {
+            // add intent
+            intentList.add(createDomainP2PIntent(intent, domainIngress, egress,
+                                                 domainLinks.build()));
+        }
+
+        return intentList.build();
+    }
+
+    /**
+     * Create a domain point to point intent from the parameters.
+     *
+     * @param originalIntent the original intent to extract the app ID and key
+     * @param ingress the ingress connection point
+     * @param egress the egress connection point
+     * @param domainLinks the list of traversed links
+     * @return the domain point to point intent
+     */
+    private static DomainPointToPointIntent createDomainP2PIntent(
+            Intent originalIntent, FilteredConnectPoint ingress,
+            FilteredConnectPoint egress, List<Link> domainLinks) {
+        return DomainPointToPointIntent.builder()
+                .appId(originalIntent.appId())
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .key(originalIntent.key())
+                .links(domainLinks)
+                .build();
+    }
+
+    /**
+     * Get links originating from the source device ID.
+     *
+     * @param links  list of available links
+     * @param source the device ID of the source device
+     * @return the list of links with the given source
+     */
+    private List<Link> getEgressLinks(Set<Link> links, final DeviceId source) {
+        return links.stream()
+                .filter(link -> link.src().deviceId().equals(source))
+                .collect(Collectors.toList());
+    }
+
 
     /**
      * Get FilteredConnectPoint from LinkCollectionIntent.
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
index b828856..d61d1eb 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.SetMultimap;
 import org.apache.felix.scr.annotations.Activate;
@@ -29,6 +30,7 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -45,16 +47,14 @@
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.resource.ResourceService;
 import org.onosproject.net.resource.impl.LabelAllocator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
+import static org.onosproject.net.domain.DomainId.LOCAL;
 import static org.onosproject.net.flow.instructions.Instruction.Type.NOACTION;
 
 /**
@@ -68,8 +68,6 @@
     private static final String UNKNOWN_INSTRUCTION = "Unknown instruction type";
     private static final String UNSUPPORTED_INSTRUCTION = "Unsupported %s instruction";
 
-    private static Logger log = LoggerFactory.getLogger(LinkCollectionIntentCompiler.class);
-
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentConfigurableRegistrator registrator;
@@ -80,6 +78,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ResourceService resourceService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DomainService domainService;
+
     private ApplicationId appId;
 
     @Activate
@@ -113,17 +114,30 @@
                                                        encapConstraint.get().encapType());
         }
 
-        List<FlowRule> rules = new ArrayList<>();
-        for (DeviceId deviceId: outputPorts.keySet()) {
-            rules.addAll(createRules(
-                    intent,
-                    deviceId,
-                    inputPorts.get(deviceId),
-                    outputPorts.get(deviceId),
-                    labels)
-            );
+        ImmutableList.Builder<Intent> intentList = ImmutableList.builder();
+        if (this.isDomainProcessingEnabled(intent)) {
+            intentList.addAll(this.getDomainIntents(intent, domainService));
         }
-        return Collections.singletonList(new FlowRuleIntent(appId, intent.key(), rules, intent.resources()));
+
+        List<FlowRule> rules = new ArrayList<>();
+        for (DeviceId deviceId : outputPorts.keySet()) {
+            // add only flows that are not inside of a domain
+            if (LOCAL.equals(domainService.getDomain(deviceId))) {
+                rules.addAll(createRules(
+                        intent,
+                        deviceId,
+                        inputPorts.get(deviceId),
+                        outputPorts.get(deviceId),
+                        labels)
+                );
+            }
+        }
+        // if any rules have been created
+        if (!rules.isEmpty()) {
+            intentList.add(new FlowRuleIntent(appId, intent.key(), rules,
+                                              intent.resources()));
+        }
+        return intentList.build();
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompiler.java
index d2bdbbc..5bd90c3 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompiler.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.SetMultimap;
@@ -31,6 +32,7 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -54,12 +56,12 @@
 import org.onosproject.net.resource.impl.LabelAllocator;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
+import static org.onosproject.net.domain.DomainId.LOCAL;
 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
 
 /**
@@ -82,6 +84,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ResourceService resourceService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DomainService domainService;
+
     private ApplicationId appId;
 
     @Activate
@@ -116,25 +121,36 @@
                                                        encapConstraint.get().encapType());
         }
 
+        ImmutableList.Builder<Intent> intentList = ImmutableList.builder();
+        if (this.isDomainProcessingEnabled(intent)) {
+            intentList.addAll(this.getDomainIntents(intent, domainService));
+        }
+
         List<Objective> objectives = new ArrayList<>();
         List<DeviceId> devices = new ArrayList<>();
-        for (DeviceId deviceId: outputPorts.keySet()) {
-            List<Objective> deviceObjectives =
-                    createRules(intent,
-                                deviceId,
-                                inputPorts.get(deviceId),
-                                outputPorts.get(deviceId),
-                                labels);
-            deviceObjectives.forEach(objective -> {
-                objectives.add(objective);
-                devices.add(deviceId);
-            });
+        for (DeviceId deviceId : outputPorts.keySet()) {
+            // add only objectives that are not inside of a domain
+            if (LOCAL.equals(domainService.getDomain(deviceId))) {
+                List<Objective> deviceObjectives =
+                        createRules(intent,
+                                    deviceId,
+                                    inputPorts.get(deviceId),
+                                    outputPorts.get(deviceId),
+                                    labels);
+                deviceObjectives.forEach(objective -> {
+                    objectives.add(objective);
+                    devices.add(deviceId);
+                });
+            }
         }
-        return Collections.singletonList(
-                new FlowObjectiveIntent(appId, intent.key(), devices,
-                                        objectives,
-                                        intent.resources(),
-                                        intent.resourceGroup()));
+        // if any objectives have been created
+        if (!objectives.isEmpty()) {
+            intentList.add(new FlowObjectiveIntent(appId, intent.key(), devices,
+                                                   objectives,
+                                                   intent.resources(),
+                                                   intent.resourceGroup()));
+        }
+        return intentList.build();
     }
 
     @Override
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
index f6e5dc8..32dc47f 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/AbstractLinkCollectionTest.java
@@ -25,6 +25,8 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.domain.DomainId;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -52,14 +54,23 @@
 
     final ApplicationId appId = new TestApplicationId("test");
 
+    final DomainId domain = DomainId.domainId("d1");
+
+    final DeviceId d2Id = DeviceId.deviceId("of:s2");
     final ConnectPoint d2p0 = connectPoint("s2", 0);
     final ConnectPoint d2p1 = connectPoint("s2", 1);
     final ConnectPoint d2p10 = connectPoint("s2", 10);
 
+    final DeviceId d3Id = DeviceId.deviceId("of:s3");
     final ConnectPoint d3p0 = connectPoint("s3", 0);
     final ConnectPoint d3p1 = connectPoint("s3", 1);
     final ConnectPoint d3p10 = connectPoint("s3", 10);
 
+    final DeviceId d4Id = DeviceId.deviceId("of:s4");
+    final ConnectPoint d4p0 = connectPoint("s4", 0);
+    final ConnectPoint d4p1 = connectPoint("s4", 1);
+    final ConnectPoint d4p10 = connectPoint("s4", 10);
+
     final DeviceId of1Id = DeviceId.deviceId("of:of1");
     final DeviceId of2Id = DeviceId.deviceId("of:of2");
     final DeviceId of3Id = DeviceId.deviceId("of:of3");
@@ -76,6 +87,7 @@
     final ConnectPoint of4p1 = connectPoint("of4", 1);
     final ConnectPoint of4p2 = connectPoint("of4", 2);
 
+    final DeviceId d1Id = DeviceId.deviceId("of:s1");
     final ConnectPoint d1p0 = connectPoint("s1", 0);
     final ConnectPoint d1p1 = connectPoint("s1", 1);
     final ConnectPoint d1p10 = connectPoint("s1", 10);
@@ -103,6 +115,12 @@
             link(d2p1, d3p1)
     );
 
+    final Set<Link> domainP2Plinks = ImmutableSet.of(
+            link(d1p0, d2p0),
+            link(d2p1, d4p1),
+            link(d4p0, d3p0)
+    );
+
     final Set<Link> linksForSp2MpCoLoc = ImmutableSet.of(
             link(d1p0, d2p0),
             link(d2p1, d3p0)
@@ -134,6 +152,7 @@
     final List<Constraint> constraintsForMPLS = mplsConstraint();
 
     CoreService coreService;
+    DomainService domainService;
     IntentExtensionService intentExtensionService;
     IntentConfigurableRegistrator registrator;
     IdGenerator idGenerator = new MockIdGenerator();
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
index 028f8bf..55c1d9b 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionEncapIntentCompilerTest.java
@@ -25,7 +25,9 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -43,13 +45,19 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.anyObject;
+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.core.Is.is;
 import static org.onlab.packet.EthType.EtherType.IPV4;
 import static org.onosproject.net.NetTestTools.APP_ID;
-import static org.onosproject.net.flow.criteria.Criterion.Type.*;
+import static org.onosproject.net.domain.DomainId.LOCAL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 
 /**
@@ -65,6 +73,10 @@
         expect(coreService.registerApplication("org.onosproject.net.intent")).andReturn(appId);
         sut.coreService = coreService;
 
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(anyObject(DeviceId.class))).andReturn(LOCAL).anyTimes();
+        sut.domainService = domainService;
+
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
 
@@ -83,7 +95,7 @@
         LinkCollectionCompiler.optimizeInstructions = false;
         LinkCollectionCompiler.copyTtl = false;
 
-        replay(coreService, intentExtensionService);
+        replay(coreService, domainService, intentExtensionService);
     }
 
     @After
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerDomainP2PTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerDomainP2PTest.java
new file mode 100644
index 0000000..1b726f7
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerDomainP2PTest.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2016-present 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.impl.compiler;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainPointToPointIntent;
+import org.onosproject.net.domain.DomainService;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.constraint.DomainConstraint;
+import org.onosproject.net.resource.MockResourceService;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+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.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.NetTestTools.link;
+import static org.onosproject.net.domain.DomainId.LOCAL;
+
+/**
+ * Those tests verify the compilation process with domains included in the path.
+ */
+public class LinkCollectionIntentCompilerDomainP2PTest extends AbstractLinkCollectionTest {
+
+    private static List<Constraint> domainConstraint =
+            ImmutableList.of(DomainConstraint.domain());
+
+    @Before
+    public void setUp() {
+        sut = new LinkCollectionIntentCompiler();
+        coreService = createMock(CoreService.class);
+        expect(coreService.registerApplication("org.onosproject.net.intent"))
+                .andReturn(appId);
+        sut.coreService = coreService;
+
+        // defining the domain assignments
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(d1Id)).andReturn(LOCAL).anyTimes();
+        expect(domainService.getDomain(d2Id)).andReturn(domain).anyTimes();
+        expect(domainService.getDomain(d4Id)).andReturn(domain).anyTimes();
+        expect(domainService.getDomain(d3Id)).andReturn(LOCAL).anyTimes();
+        sut.domainService = domainService;
+
+        Intent.unbindIdGenerator(idGenerator);
+        Intent.bindIdGenerator(idGenerator);
+
+        intentExtensionService = createMock(IntentExtensionService.class);
+        intentExtensionService
+                .registerCompiler(LinkCollectionIntent.class, sut);
+        intentExtensionService.unregisterCompiler(LinkCollectionIntent.class);
+
+        registrator = new IntentConfigurableRegistrator();
+        registrator.extensionService = intentExtensionService;
+        registrator.cfgService = new ComponentConfigAdapter();
+        registrator.activate();
+
+        sut.registrator = registrator;
+        sut.resourceService = new MockResourceService();
+
+        LinkCollectionCompiler.optimizeInstructions = false;
+        LinkCollectionCompiler.copyTtl = false;
+
+        replay(coreService, domainService, intentExtensionService);
+    }
+
+    @After
+    public void tearDown() {
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * We test the proper compilation of one domain device.
+     */
+    @Test
+    public void testCompilationSingleDeviceDomainP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(p2pLinks)
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d3p10)
+                ))
+                .constraints(domainConstraint)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(2));
+
+        DomainPointToPointIntent domainIntent =
+                ((DomainPointToPointIntent) compiled.get(0));
+        ConnectPoint ingress =
+                domainIntent.filteredIngressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(ingress, equalTo(d2p0));
+        ConnectPoint egress =
+                domainIntent.filteredEgressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(egress, equalTo(d2p1));
+        assertThat(domainIntent.links(), hasSize(0));
+
+        Collection<FlowRule> rules =
+                ((FlowRuleIntent) compiled.get(1)).flowRules();
+        assertThat(rules, hasSize(2));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of a domain with two devices.
+     */
+    @Test
+    public void testCompilationMultiHopDomainP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(domainP2Plinks)
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d3p10)
+                ))
+                .constraints(domainConstraint)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(2));
+
+        DomainPointToPointIntent domainIntent =
+                ((DomainPointToPointIntent) compiled.get(0));
+        ConnectPoint ingress =
+                domainIntent.filteredIngressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(ingress, equalTo(d2p0));
+        ConnectPoint egress =
+                domainIntent.filteredEgressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(egress, equalTo(d4p0));
+        assertThat(domainIntent.links(), hasSize(1));
+
+        Collection<FlowRule> rules =
+                ((FlowRuleIntent) compiled.get(1)).flowRules();
+        assertThat(rules, hasSize(2));
+
+        sut.deactivate();
+
+    }
+
+
+    /**
+     * We test the proper compilation of a domain starting with a domain device.
+     */
+    @Test
+    public void testCompilationDomainStartP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of(
+                        link(d2p0, d1p0)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10)
+                ))
+                .constraints(domainConstraint)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(2));
+
+        DomainPointToPointIntent domainIntent =
+                ((DomainPointToPointIntent) compiled.get(0));
+        ConnectPoint ingress =
+                domainIntent.filteredIngressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(ingress, equalTo(d2p10));
+        ConnectPoint egress =
+                domainIntent.filteredEgressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(egress, equalTo(d2p0));
+        assertThat(domainIntent.links(), hasSize(0));
+
+        Collection<FlowRule> rules =
+                ((FlowRuleIntent) compiled.get(1)).flowRules();
+        assertThat(rules, hasSize(1));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of a domain ending with a domain device.
+     */
+    @Test
+    public void testCompilationDomainEndP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of(
+                        link(d1p0, d2p0)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d1p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .constraints(domainConstraint)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(2));
+
+        DomainPointToPointIntent domainIntent =
+                ((DomainPointToPointIntent) compiled.get(0));
+        ConnectPoint ingress =
+                domainIntent.filteredIngressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(ingress, equalTo(d2p0));
+        ConnectPoint egress =
+                domainIntent.filteredEgressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(egress, equalTo(d2p10));
+        assertThat(domainIntent.links(), hasSize(0));
+
+        Collection<FlowRule> rules =
+                ((FlowRuleIntent) compiled.get(1)).flowRules();
+        assertThat(rules, hasSize(1));
+
+        sut.deactivate();
+
+    }
+
+    /**
+     * We test the proper compilation of a path fully inside of a domain.
+     */
+    @Test
+    public void testCompilationDomainFullP2P() {
+
+        intent = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .applyTreatmentOnEgress(true)
+                .links(ImmutableSet.of(
+                        link(d2p0, d4p0)
+                ))
+                .filteredIngressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d2p10)
+                ))
+                .filteredEgressPoints(ImmutableSet.of(
+                        new FilteredConnectPoint(d4p10)
+                ))
+                .constraints(domainConstraint)
+                .build();
+
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intent, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+        DomainPointToPointIntent domainIntent =
+                ((DomainPointToPointIntent) compiled.get(0));
+        ConnectPoint ingress =
+                domainIntent.filteredIngressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(ingress, equalTo(d2p10));
+        ConnectPoint egress =
+                domainIntent.filteredEgressPoints().iterator().next()
+                        .connectPoint();
+        assertThat(egress, equalTo(d4p10));
+        assertThat(domainIntent.links(), hasSize(1));
+
+        sut.deactivate();
+
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerP2PTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerP2PTest.java
index 44754a6..2b93ce9 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerP2PTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerP2PTest.java
@@ -26,7 +26,9 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -43,6 +45,7 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
@@ -50,9 +53,10 @@
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.core.Is.is;
 import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.domain.DomainId.LOCAL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.*;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 
 /**
  * This set of tests are meant to test the proper compilation
@@ -68,6 +72,10 @@
                 .andReturn(appId);
         sut.coreService = coreService;
 
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(anyObject(DeviceId.class))).andReturn(LOCAL).anyTimes();
+        sut.domainService = domainService;
+
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
 
@@ -86,7 +94,7 @@
         LinkCollectionCompiler.optimizeInstructions = false;
         LinkCollectionCompiler.copyTtl = false;
 
-        replay(coreService, intentExtensionService);
+        replay(coreService, domainService, intentExtensionService);
     }
 
     @After
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
index f70c69b..d5aea2c 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
@@ -26,9 +26,11 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -49,6 +51,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
@@ -60,11 +63,13 @@
 import static org.hamcrest.Matchers.is;
 import static org.onlab.packet.EthType.EtherType.IPV4;
 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.domain.DomainId.LOCAL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
 import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.*;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 
 /**
  * This set of tests are meant to test the LinkCollectionIntent
@@ -80,6 +85,10 @@
                 .andReturn(appId);
         sut.coreService = coreService;
 
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(anyObject(DeviceId.class))).andReturn(LOCAL).anyTimes();
+        sut.domainService = domainService;
+
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
 
@@ -107,7 +116,7 @@
         LinkCollectionCompiler.optimizeInstructions = false;
         LinkCollectionCompiler.copyTtl = false;
 
-        replay(coreService, intentExtensionService);
+        replay(coreService, domainService, intentExtensionService);
 
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompilerTest.java
index 613c3cb..2c77956 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentObjectiveCompilerTest.java
@@ -25,9 +25,11 @@
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -50,7 +52,10 @@
 import java.util.List;
 import java.util.Set;
 
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -59,6 +64,7 @@
 import static org.hamcrest.Matchers.nullValue;
 import static org.onosproject.net.Link.Type.DIRECT;
 import static org.onosproject.net.NetTestTools.PID;
+import static org.onosproject.net.domain.DomainId.LOCAL;
 
 public class LinkCollectionIntentObjectiveCompilerTest extends AbstractLinkCollectionTest {
 
@@ -82,6 +88,11 @@
         compiler.coreService = coreService;
         compiler.flowObjectiveService = flowObjectiveService;
 
+
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(anyObject(DeviceId.class))).andReturn(LOCAL).anyTimes();
+        compiler.domainService = domainService;
+
         Intent.bindIdGenerator(idGenerator);
 
         intentExtensionService = createMock(IntentExtensionService.class);
@@ -99,7 +110,7 @@
         LinkCollectionCompiler.optimizeInstructions = false;
         LinkCollectionCompiler.copyTtl = false;
 
-        replay(coreService, intentExtensionService);
+        replay(coreService, domainService, intentExtensionService);
 
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java
index 8038619..93b83fa 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionOptimizationTest.java
@@ -25,7 +25,9 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -42,13 +44,18 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.anyObject;
+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.core.Is.is;
 import static org.onlab.packet.EthType.EtherType.IPV4;
-import static org.onosproject.net.NetTestTools.*;
-import static org.onosproject.net.flow.criteria.Criterion.Type.*;
+import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.domain.DomainId.LOCAL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 
 /**
@@ -65,6 +72,10 @@
                 .andReturn(appId);
         sut.coreService = coreService;
 
+        domainService = createMock(DomainService.class);
+        expect(domainService.getDomain(anyObject(DeviceId.class))).andReturn(LOCAL).anyTimes();
+        sut.domainService = domainService;
+
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
 
@@ -86,7 +97,7 @@
         LinkCollectionCompiler.optimizeInstructions = true;
         LinkCollectionCompiler.copyTtl = true;
 
-        replay(coreService, intentExtensionService);
+        replay(coreService, domainService, intentExtensionService);
     }
 
     @After