ONOS-4383 Updating traffic selector for {MP2SP,SP2MP}IntentCompilers

Apply egress action to ingress selector for non-ingress devices

Change-Id: I98b4c591d09cc4b5d0e0ff89eaeac44ba07e6326
diff --git a/cli/src/main/java/org/onosproject/cli/net/AddSinglePointToMultiPointIntentCommand.java b/cli/src/main/java/org/onosproject/cli/net/AddSinglePointToMultiPointIntentCommand.java
index 30133bd..87ede2b 100644
--- a/cli/src/main/java/org/onosproject/cli/net/AddSinglePointToMultiPointIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/AddSinglePointToMultiPointIntentCommand.java
@@ -18,7 +18,6 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.intent.Constraint;
@@ -59,7 +58,7 @@
         }
 
         TrafficSelector selector = buildTrafficSelector();
-        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        TrafficTreatment treatment = buildTrafficTreatment();
         List<Constraint> constraints = buildConstraints();
 
         SinglePointToMultiPointIntent intent =
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 04e740d..d582f6e 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
@@ -22,6 +22,8 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
@@ -34,10 +36,25 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.impl.IntentCompilationException;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -109,15 +126,20 @@
                 .forEach(ingressTreatmentBuilder::setOutput);
         TrafficTreatment ingressTreatment = ingressTreatmentBuilder.build();
 
+        TrafficSelector defaultTrafficSelector = applyTreatmentToSelector(intent.selector(), ingressTreatment);
+
         List<FlowRule> rules = new ArrayList<>(inPorts.size());
         for (PortNumber inPort: inPorts) {
-            TrafficSelector selector = DefaultTrafficSelector.builder(intent.selector()).matchInPort(inPort).build();
+            TrafficSelector.Builder selectorBuilder;
             TrafficTreatment treatment;
             if (ingressPorts.contains(inPort)) {
+                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
                 treatment = ingressTreatment;
             } else {
+                selectorBuilder = DefaultTrafficSelector.builder(defaultTrafficSelector);
                 treatment = defaultTreatment;
             }
+            TrafficSelector selector = selectorBuilder.matchInPort(inPort).build();
 
             FlowRule rule = DefaultFlowRule.builder()
                                 .forDevice(deviceId)
@@ -132,4 +154,169 @@
 
         return rules;
     }
+
+    private TrafficSelector applyTreatmentToSelector(TrafficSelector selector, TrafficTreatment treatment) {
+        TrafficSelector.Builder defaultSelectorBuilder = DefaultTrafficSelector.builder(selector);
+        treatment.allInstructions().forEach(instruction -> {
+            switch (instruction.type()) {
+                case L0MODIFICATION:
+                case L1MODIFICATION:
+                    throw new IntentCompilationException("L0 and L1 mods not supported");
+                case L2MODIFICATION:
+                    L2ModificationInstruction l2mod = (L2ModificationInstruction) instruction;
+                    switch (l2mod.subtype()) {
+                        case ETH_SRC:
+                        case ETH_DST:
+                            ModEtherInstruction ethInstr = (ModEtherInstruction) l2mod;
+                            switch (ethInstr.subtype()) {
+                                case ETH_SRC:
+                                    defaultSelectorBuilder.matchEthSrc(ethInstr.mac());
+                                    break;
+                                case ETH_DST:
+                                    defaultSelectorBuilder.matchEthDst(ethInstr.mac());
+                                    break;
+                                default:
+                                    throw new IntentCompilationException("Bad eth subtype");
+                            }
+                            break;
+                        case VLAN_ID:
+                            ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2mod;
+                            defaultSelectorBuilder.matchVlanId(vlanIdInstr.vlanId());
+                            break;
+                        case VLAN_PUSH:
+                            //FIXME
+                            break;
+                        case VLAN_POP:
+                            //TODO how do we handle dropped label? remove the selector?
+                            throw new IntentCompilationException("Can't handle pop label");
+                        case VLAN_PCP:
+                            ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2mod;
+                            defaultSelectorBuilder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
+                            break;
+                        case MPLS_LABEL:
+                        case MPLS_PUSH:
+                            //FIXME
+                            ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2mod;
+                            defaultSelectorBuilder.matchMplsLabel(mplsInstr.label());
+                            break;
+                        case MPLS_POP:
+                            //TODO how do we handle dropped label? remove the selector?
+                            throw new IntentCompilationException("Can't handle pop label");
+                        case DEC_MPLS_TTL:
+                            // no-op
+                            break;
+                        case MPLS_BOS:
+                            ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2mod;
+                            defaultSelectorBuilder.matchMplsBos(mplsBosInstr.mplsBos());
+                            break;
+                        case TUNNEL_ID:
+                            ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2mod;
+                            defaultSelectorBuilder.matchTunnelId(tunInstr.tunnelId());
+                            break;
+                        default:
+                            throw new IntentCompilationException("Unknown L2 Modification instruction");
+                    }
+                    break;
+                case L3MODIFICATION:
+                    L3ModificationInstruction l3mod = (L3ModificationInstruction) instruction;
+                    // TODO check ethernet proto
+                    switch (l3mod.subtype()) {
+                        case IPV4_SRC:
+                        case IPV4_DST:
+                        case IPV6_SRC:
+                        case IPV6_DST:
+                            ModIPInstruction ipInstr = (ModIPInstruction) l3mod;
+                            // TODO check if ip falls in original prefix
+                            IpPrefix prefix = ipInstr.ip().toIpPrefix();
+                            switch (ipInstr.subtype()) {
+                                case IPV4_SRC:
+                                    defaultSelectorBuilder.matchIPSrc(prefix);
+                                    break;
+                                case IPV4_DST:
+                                    defaultSelectorBuilder.matchIPSrc(prefix);
+                                    break;
+                                case IPV6_SRC:
+                                    defaultSelectorBuilder.matchIPv6Src(prefix);
+                                    break;
+                                case IPV6_DST:
+                                    defaultSelectorBuilder.matchIPv6Dst(prefix);
+                                    break;
+                                default:
+                                    throw new IntentCompilationException("Bad type for IP instruction");
+                            }
+                            break;
+                        case IPV6_FLABEL:
+                            ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3mod;
+                            defaultSelectorBuilder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
+                            break;
+                        case DEC_TTL:
+                            // no-op
+                            break;
+                        case TTL_OUT:
+                            // no-op
+                            break;
+                        case TTL_IN:
+                            // no-op
+                            break;
+                        case ARP_SPA:
+                            ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3mod;
+                            if (arpIpInstr.ip().isIp4()) {
+                                defaultSelectorBuilder.matchArpSpa((Ip4Address) arpIpInstr.ip());
+                            } else {
+                                throw new IntentCompilationException("IPv6 not supported for ARP");
+                            }
+                            break;
+                        case ARP_SHA:
+                            ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3mod;
+                            defaultSelectorBuilder.matchArpSha(arpEthInstr.mac());
+                            break;
+                        case ARP_OP:
+                            ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3mod;
+                            //FIXME is the long to int cast safe?
+                            defaultSelectorBuilder.matchArpOp((int) arpOpInstr.op());
+                            break;
+                        default:
+                            throw new IntentCompilationException("Unknown L3 Modification instruction");
+                    }
+                    break;
+                case L4MODIFICATION:
+                    if (instruction instanceof ModTransportPortInstruction) {
+                        // TODO check IP proto
+                        ModTransportPortInstruction l4mod = (ModTransportPortInstruction) instruction;
+                        switch (l4mod.subtype()) {
+                            case TCP_SRC:
+                                defaultSelectorBuilder.matchTcpSrc(l4mod.port());
+                                break;
+                            case TCP_DST:
+                                defaultSelectorBuilder.matchTcpDst(l4mod.port());
+                                break;
+                            case UDP_SRC:
+                                defaultSelectorBuilder.matchUdpSrc(l4mod.port());
+                                break;
+                            case UDP_DST:
+                                defaultSelectorBuilder.matchUdpDst(l4mod.port());
+                                break;
+                            default:
+                                throw new IntentCompilationException("Unknown L4 Modification instruction");
+                        }
+                    } else {
+                        throw new IntentCompilationException("Unknown L4 Modification instruction");
+                    }
+                    break;
+                case NOACTION:
+                case OUTPUT:
+                case GROUP:
+                case QUEUE:
+                case TABLE:
+                case METER:
+                case METADATA:
+                case EXTENSION: // TODO is extension no-op or unsupported?
+                    // Nothing to do
+                    break;
+                default:
+                    throw new IntentCompilationException("Unknown instruction type");
+            }
+        });
+        return defaultSelectorBuilder.build();
+    }
 }