ONOS-4802 sp2mp intents now apply treatment at the egress switch

Change-Id: Ibdd675f331e522c8b9f1d0e2e9fd5d6b93162fd1
diff --git a/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java b/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
index f7ca15c..f72354d 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
@@ -39,6 +39,7 @@
 
     private final Set<ConnectPoint> ingressPoints;
     private final Set<ConnectPoint> egressPoints;
+    private final boolean egressTreatmentFlag;
 
     /**
      * Creates a new actionable intent capable of funneling the selected
@@ -54,21 +55,23 @@
      * @param egressPoints egress points
      * @param constraints optional list of constraints
      * @param priority    priority to use for the flows generated by this intent
+     * @param egressTreatment true if treatment should be applied by the egress device
      * @throws NullPointerException {@code path} is null
      */
     private LinkCollectionIntent(ApplicationId appId,
-                                Key key,
-                                TrafficSelector selector,
-                                TrafficTreatment treatment,
-                                Set<Link> links,
-                                Set<ConnectPoint> ingressPoints,
-                                Set<ConnectPoint> egressPoints,
-                                List<Constraint> constraints,
-                                int priority) {
+                                 Key key,
+                                 TrafficSelector selector,
+                                 TrafficTreatment treatment,
+                                 Set<Link> links,
+                                 Set<ConnectPoint> ingressPoints,
+                                 Set<ConnectPoint> egressPoints,
+                                 List<Constraint> constraints,
+                                 int priority, boolean egressTreatment) {
         super(appId, key, resources(links), selector, treatment, constraints, priority);
         this.links = links;
         this.ingressPoints = ingressPoints;
         this.egressPoints = egressPoints;
+        this.egressTreatmentFlag = egressTreatment;
     }
 
     /**
@@ -79,6 +82,7 @@
         this.links = null;
         this.ingressPoints = null;
         this.egressPoints = null;
+        this.egressTreatmentFlag = false;
     }
 
     /**
@@ -100,6 +104,7 @@
         Set<Link> links;
         Set<ConnectPoint> ingressPoints;
         Set<ConnectPoint> egressPoints;
+        boolean egressTreatmentFlag;
 
         private Builder() {
             // Hide constructor
@@ -171,6 +176,17 @@
             return this;
         }
 
+        /**
+         * Sets the intent to apply treatment at the egress rather than the
+         * ingress.
+         *
+         * @param treatmentOnEgress true applies treatment on egress device
+         * @return this builder
+         */
+        public Builder applyTreatmentOnEgress(boolean treatmentOnEgress) {
+            this.egressTreatmentFlag = treatmentOnEgress;
+            return this;
+        }
 
         /**
          * Builds a single point to multi point intent from the
@@ -189,7 +205,8 @@
                     ingressPoints,
                     egressPoints,
                     constraints,
-                    priority
+                    priority,
+                    egressTreatmentFlag
             );
         }
     }
@@ -223,6 +240,15 @@
         return egressPoints;
     }
 
+    /**
+     * Returns whether treatment should be applied on egress.
+     *
+     * @return the egress treatment flag
+     */
+    public boolean applyTreatmentOnEgress() {
+        return egressTreatmentFlag;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
@@ -236,6 +262,7 @@
                 .add("links", links())
                 .add("ingress", ingressPoints())
                 .add("egress", egressPoints())
+                .add("treatementOnEgress", applyTreatmentOnEgress())
                 .toString();
     }
-}
+}
\ No newline at end of file
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 d582f6e..9fd7b12 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
@@ -111,51 +111,75 @@
 
     private List<FlowRule> createRules(LinkCollectionIntent intent, DeviceId deviceId,
                                        Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-        Set<PortNumber> ingressPorts = intent.ingressPoints().stream()
-                .filter(point -> point.deviceId().equals(deviceId))
-                .map(ConnectPoint::port)
-                .collect(Collectors.toSet());
-
         TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
         outPorts.stream()
                 .forEach(defaultTreatmentBuilder::setOutput);
-        TrafficTreatment defaultTreatment = defaultTreatmentBuilder.build();
+        TrafficTreatment outputOnlyTreatment = defaultTreatmentBuilder.build();
+        Set<PortNumber> ingressPorts = Collections.emptySet();
+        Set<PortNumber> egressPorts = Collections.emptySet();
 
-        TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
-        outPorts.stream()
-                .forEach(ingressTreatmentBuilder::setOutput);
-        TrafficTreatment ingressTreatment = ingressTreatmentBuilder.build();
-
-        TrafficSelector defaultTrafficSelector = applyTreatmentToSelector(intent.selector(), ingressTreatment);
+        if (!intent.applyTreatmentOnEgress()) {
+            ingressPorts = intent.ingressPoints().stream()
+                    .filter(point -> point.deviceId().equals(deviceId))
+                    .map(ConnectPoint::port)
+                    .collect(Collectors.toSet());
+        } else {
+            egressPorts = intent.egressPoints().stream()
+                    .filter(point -> point.deviceId().equals(deviceId))
+                    .map(ConnectPoint::port)
+                    .collect(Collectors.toSet());
+        }
 
         List<FlowRule> rules = new ArrayList<>(inPorts.size());
         for (PortNumber inPort: inPorts) {
             TrafficSelector.Builder selectorBuilder;
             TrafficTreatment treatment;
-            if (ingressPorts.contains(inPort)) {
-                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-                treatment = ingressTreatment;
+            TrafficTreatment intentTreatment;
+
+            if (!intent.applyTreatmentOnEgress()) {
+                TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
+                outPorts.stream()
+                        .forEach(ingressTreatmentBuilder::setOutput);
+                intentTreatment = ingressTreatmentBuilder.build();
+
+                if (ingressPorts.contains(inPort)) {
+                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                    treatment = intentTreatment;
+                } else {
+                    selectorBuilder = applyTreatmentToSelector(intent.selector(), intentTreatment);
+                    treatment = outputOnlyTreatment;
+                }
             } else {
-                selectorBuilder = DefaultTrafficSelector.builder(defaultTrafficSelector);
-                treatment = defaultTreatment;
+                if (outPorts.stream().allMatch(egressPorts::contains)) {
+                    TrafficTreatment.Builder egressTreatmentBuilder =
+                            DefaultTrafficTreatment.builder(intent.treatment());
+                    outPorts.stream()
+                            .forEach(egressTreatmentBuilder::setOutput);
+
+                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                    treatment = egressTreatmentBuilder.build();
+                } else {
+                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                    treatment = outputOnlyTreatment;
+                }
             }
             TrafficSelector selector = selectorBuilder.matchInPort(inPort).build();
 
             FlowRule rule = DefaultFlowRule.builder()
-                                .forDevice(deviceId)
-                                .withSelector(selector)
-                                .withTreatment(treatment)
-                                .withPriority(intent.priority())
-                                .fromApp(appId)
-                                .makePermanent()
-                                .build();
+                    .forDevice(deviceId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(intent.priority())
+                    .fromApp(appId)
+                    .makePermanent()
+                    .build();
             rules.add(rule);
         }
 
         return rules;
     }
 
-    private TrafficSelector applyTreatmentToSelector(TrafficSelector selector, TrafficTreatment treatment) {
+    private TrafficSelector.Builder applyTreatmentToSelector(TrafficSelector selector, TrafficTreatment treatment) {
         TrafficSelector.Builder defaultSelectorBuilder = DefaultTrafficSelector.builder(selector);
         treatment.allInstructions().forEach(instruction -> {
             switch (instruction.type()) {
@@ -317,6 +341,6 @@
                     throw new IntentCompilationException("Unknown instruction type");
             }
         });
-        return defaultSelectorBuilder.build();
+        return defaultSelectorBuilder;
     }
-}
+}
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java
index 2508654..0da2194 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java
@@ -75,9 +75,10 @@
                 .ingressPoints(ImmutableSet.of(intent.ingressPoint()))
                 .egressPoints(intent.egressPoints())
                 .priority(intent.priority())
+                .applyTreatmentOnEgress(true)
                 .constraints(intent.constraints())
                 .build();
 
         return Collections.singletonList(result);
     }
-}
+}
\ No newline at end of file