[ONOS-7129] Pipeliner for fabric pipeline

Change-Id: I86b44694e1251611359e8ddc8be2533a741230cc
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java
new file mode 100644
index 0000000..d37402d
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.pipelines.fabric.pipeliner;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+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.EthCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.pipelines.fabric.FabricConstants;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handling forwarding objective for fabric pipeliner.
+ */
+public class FabricForwardingPipeliner {
+    private static final Logger log = getLogger(FabricForwardingPipeliner.class);
+
+    protected DeviceId deviceId;
+
+    public FabricForwardingPipeliner(DeviceId deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public PipelinerTranslationResult forward(ForwardingObjective forwardObjective) {
+        PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
+        if (forwardObjective.flag() == ForwardingObjective.Flag.VERSATILE) {
+            processVersatileFwd(forwardObjective, resultBuilder);
+        } else {
+            processSpecificFwd(forwardObjective, resultBuilder);
+        }
+        return resultBuilder.build();
+    }
+
+    private void processVersatileFwd(ForwardingObjective fwd,
+                                     PipelinerTranslationResult.Builder resultBuilder) {
+        // program ACL table only
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .withSelector(fwd.selector())
+                .withTreatment(fwd.treatment())
+                .forTable(FabricConstants.TBL_ACL_ID)
+                .withPriority(fwd.priority())
+                .forDevice(deviceId)
+                .makePermanent()
+                .fromApp(fwd.appId())
+                .build();
+        resultBuilder.addFlowRule(flowRule);
+    }
+
+    private void processSpecificFwd(ForwardingObjective fwd,
+                                    PipelinerTranslationResult.Builder resultBuilder) {
+        TrafficSelector selector = fwd.selector();
+        TrafficSelector meta = fwd.meta();
+
+        ImmutableSet.Builder<Criterion> criterionSetBuilder = ImmutableSet.builder();
+        criterionSetBuilder.addAll(selector.criteria());
+
+        if (meta != null) {
+            criterionSetBuilder.addAll(meta.criteria());
+        }
+
+        Set<Criterion> criteria = criterionSetBuilder.build();
+
+        VlanIdCriterion vlanIdCriterion = null;
+        EthCriterion ethDstCriterion = null;
+
+        for (Criterion criterion : criteria) {
+            switch (criterion.type()) {
+                case ETH_DST:
+                    ethDstCriterion = (EthCriterion) criterion;
+                    break;
+                case VLAN_VID:
+                    vlanIdCriterion = (VlanIdCriterion) criterion;
+                    break;
+                default:
+                    log.warn("Unsupported criterion {}", criterion);
+                    break;
+            }
+        }
+
+        ForwardingFunctionType forwardingFunctionType =
+                ForwardingFunctionType.getForwardingFunctionType(fwd);
+        switch (forwardingFunctionType) {
+            case L2_UNICAST:
+                processL2UnicastRule(vlanIdCriterion, ethDstCriterion, fwd, resultBuilder);
+                break;
+            case L2_BROADCAST:
+                processL2BroadcastRule(vlanIdCriterion, fwd, resultBuilder);
+                break;
+            case IPV4_UNICAST:
+            case IPV4_MULTICAST:
+            case IPV6_UNICAST:
+            case IPV6_MULTICAST:
+            case MPLS:
+            default:
+                log.warn("Unsupported forwarding function type {}", criteria);
+                resultBuilder.setError(ObjectiveError.UNSUPPORTED);
+                break;
+        }
+    }
+
+    // L2 Unicast: learnt mac address + vlan
+    private void processL2UnicastRule(VlanIdCriterion vlanIdCriterion,
+                                      EthCriterion ethDstCriterion,
+                                      ForwardingObjective fwd,
+                                      PipelinerTranslationResult.Builder resultBuilder) {
+        checkNotNull(vlanIdCriterion, "VlanId criterion should not be null");
+        checkNotNull(ethDstCriterion, "EthDst criterion should not be null");
+
+        if (fwd.nextId() == null) {
+            log.warn("Forwarding objective for L2 unicast should contains next id");
+            resultBuilder.setError(ObjectiveError.BADPARAMS);
+            return;
+        }
+
+        VlanId vlanId = vlanIdCriterion.vlanId();
+        MacAddress ethDst = ethDstCriterion.mac();
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(vlanId)
+                .matchEthDst(ethDst)
+                .build();
+        TrafficTreatment treatment = buildSetNextIdTreatment(fwd.nextId());
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(fwd.appId())
+                .withPriority(fwd.priority())
+                .makePermanent()
+                .forDevice(deviceId)
+                .forTable(FabricConstants.TBL_BRIDGING_ID)
+                .build();
+
+        resultBuilder.addFlowRule(flowRule);
+    }
+
+    private void processL2BroadcastRule(VlanIdCriterion vlanIdCriterion,
+                                        ForwardingObjective fwd,
+                                        PipelinerTranslationResult.Builder resultBuilder) {
+        checkNotNull(vlanIdCriterion, "VlanId criterion should not be null");
+        if (fwd.nextId() == null) {
+            log.warn("Forwarding objective for L2 broadcast should contains next id");
+            resultBuilder.setError(ObjectiveError.BADPARAMS);
+            return;
+        }
+
+        VlanId vlanId = vlanIdCriterion.vlanId();
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(vlanId)
+                .build();
+        TrafficTreatment treatment = buildSetNextIdTreatment(fwd.nextId());
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(fwd.appId())
+                .withPriority(fwd.priority())
+                .makePermanent()
+                .forDevice(deviceId)
+                .forTable(FabricConstants.TBL_BRIDGING_ID)
+                .build();
+
+        resultBuilder.addFlowRule(flowRule);
+    }
+
+    private static TrafficTreatment buildSetNextIdTreatment(Integer nextId) {
+        PiActionParam nextIdParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_ID_ID,
+                                                      ImmutableByteSequence.copyFrom(nextId.byteValue()));
+        PiAction nextIdAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_NEXT_ID_ID)
+                .withParameter(nextIdParam)
+                .build();
+
+        return DefaultTrafficTreatment.builder()
+                .piTableAction(nextIdAction)
+                .build();
+    }
+
+}