[ONOS-7129] Pipeliner for fabric pipeline

Change-Id: I86b44694e1251611359e8ddc8be2533a741230cc
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
new file mode 100644
index 0000000..69ec0ab
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
@@ -0,0 +1,244 @@
+/*
+ * 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.Sets;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+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.PiCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flowobjective.FilteringObjective;
+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.Collection;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handling filtering objective for fabric pipeliner.
+ */
+public class FabricFilteringPipeliner {
+    private static final Logger log = getLogger(FabricFilteringPipeliner.class);
+    // Forwarding types
+    private static final byte FWD_BRIDGING = 0;
+    private static final byte FWD_MPLS = 1;
+    private static final byte FWD_IPV4_UNICAST = 2;
+    private static final byte FWD_IPV4_MULTICAST = 3;
+    private static final byte FWD_IPV6_UNICAST = 4;
+    private static final byte FWD_IPV6_MULTICAST = 5;
+    private static final PiCriterion VLAN_VALID = PiCriterion.builder()
+            .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{1})
+            .build();
+    private static final PiCriterion VLAN_INVALID = PiCriterion.builder()
+            .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{0})
+            .build();
+
+    protected DeviceId deviceId;
+
+    public FabricFilteringPipeliner(DeviceId deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    /**
+     * Translates filtering objective to flows and groups.
+     *
+     * @param filterObjective the filtering objective
+     * @return translation result, contains flows, groups or error it generated
+     */
+    public PipelinerTranslationResult filter(FilteringObjective filterObjective) {
+        PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
+        // maps selector and treatment from filtering objective to filtering
+        // control block.
+
+        if (filterObjective.type() == FilteringObjective.Type.DENY) {
+            log.warn("Unsupported filtering objective type {}", filterObjective.type());
+            resultBuilder.setError(ObjectiveError.UNSUPPORTED);
+            return resultBuilder.build();
+        }
+
+        if (filterObjective.key() == null ||
+                filterObjective.key().type() != Criterion.Type.IN_PORT) {
+            log.warn("Unsupported filter key {}", filterObjective.key());
+            resultBuilder.setError(ObjectiveError.BADPARAMS);
+            return resultBuilder.build();
+        }
+        PortCriterion inPortCriterion = (PortCriterion) filterObjective.key();
+        VlanIdCriterion vlanCriterion = filterObjective.conditions().stream()
+                .filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
+                .map(criterion -> (VlanIdCriterion) criterion)
+                .findFirst()
+                .orElse(null);
+        EthCriterion ethDstCriterion = filterObjective.conditions().stream()
+                .filter(criterion -> criterion.type() == Criterion.Type.ETH_DST)
+                .map(criterion -> (EthCriterion) criterion)
+                .findFirst()
+                .orElse(null);
+
+        FlowRule inPortVlanTableRule = createInPortVlanTable(inPortCriterion, vlanCriterion,
+                                                             filterObjective);
+        Collection<FlowRule> fwdClassifierRules = createFwdClassifierRules(inPortCriterion, ethDstCriterion,
+                                                                           filterObjective);
+
+        resultBuilder.addFlowRule(inPortVlanTableRule);
+        fwdClassifierRules.forEach(resultBuilder::addFlowRule);
+        return resultBuilder.build();
+    }
+
+    private FlowRule createInPortVlanTable(Criterion inPortCriterion,
+                                           VlanIdCriterion vlanCriterion,
+                                           FilteringObjective filterObjective) {
+        Criterion vlanIsVlalidCriterion;
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+                .add(inPortCriterion);
+
+        VlanId vlanId = null;
+        if (vlanCriterion != null) {
+            vlanId = vlanCriterion.vlanId();
+        }
+
+        vlanIsVlalidCriterion = VLAN_VALID;
+        if (vlanId == null || vlanId.equals(VlanId.NONE)) {
+            // untag vlan, match in port only
+            vlanIsVlalidCriterion = VLAN_INVALID;
+        }
+
+        selector.add(vlanIsVlalidCriterion);
+
+        // TODO: check if this treatment is valid or not
+        TrafficTreatment treatment = filterObjective.meta();
+        if (treatment == null) {
+            treatment = DefaultTrafficTreatment.emptyTreatment();
+        }
+
+        return DefaultFlowRule.builder()
+                .fromApp(filterObjective.appId())
+                .withPriority(filterObjective.priority())
+                .withSelector(selector.build())
+                .withTreatment(treatment)
+                .withPriority(filterObjective.priority())
+                .forTable(FabricConstants.TBL_INGRESS_PORT_VLAN_ID)
+                .forDevice(deviceId)
+                .makePermanent()
+                .build();
+    }
+
+    private Collection<FlowRule> createFwdClassifierRules(PortCriterion inPortCriterion,
+                                                          EthCriterion ethDstCriterion,
+                                                          FilteringObjective filterObjective) {
+        Collection<FlowRule> flowRules = Sets.newHashSet();
+        if (ethDstCriterion == null) {
+            // Bridging table, do nothing
+            return flowRules;
+        }
+        PortNumber port = inPortCriterion.port();
+        MacAddress dstMac = ethDstCriterion.mac();
+        if (dstMac.isMulticast()) {
+            flowRules.add(createMulticastFwdClassifierRule(port, dstMac, filterObjective));
+            return flowRules;
+        }
+
+        flowRules.addAll(createIpFwdClassifierRules(port, dstMac, filterObjective));
+        flowRules.add(createMplsFwdClassifierRule(port, dstMac, filterObjective));
+        return flowRules;
+    }
+
+    private FlowRule createMulticastFwdClassifierRule(PortNumber inPort, MacAddress dstMac,
+                                                      FilteringObjective filterObjective) {
+        TrafficTreatment treatment;
+        short ethType;
+        if (dstMac.equals(MacAddress.IPV4_MULTICAST)) {
+            // Ipv4 multicast
+            treatment = createFwdClassifierTreatment(FWD_IPV4_MULTICAST);
+            ethType = Ethernet.TYPE_IPV4;
+        } else {
+            // IPv6 multicast
+            treatment = createFwdClassifierTreatment(FWD_IPV6_MULTICAST);
+            ethType = Ethernet.TYPE_IPV6;
+        }
+        return createFwdClassifierRule(inPort, ethType, dstMac, treatment, filterObjective);
+    }
+
+    private Collection<FlowRule> createIpFwdClassifierRules(PortNumber inPort,
+                                                            MacAddress dstMac,
+                                                            FilteringObjective filterObjective) {
+        Collection<FlowRule> flowRules = Sets.newHashSet();
+        TrafficTreatment treatment;
+        treatment = createFwdClassifierTreatment(FWD_IPV4_UNICAST);
+        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV4, dstMac, treatment, filterObjective));
+        treatment = createFwdClassifierTreatment(FWD_IPV6_UNICAST);
+        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV6, dstMac, treatment, filterObjective));
+        return flowRules;
+    }
+
+    private FlowRule createMplsFwdClassifierRule(PortNumber inPort,
+                                                 MacAddress dstMac,
+                                                 FilteringObjective filterObjective) {
+        TrafficTreatment treatment = createFwdClassifierTreatment(FWD_MPLS);
+        return createFwdClassifierRule(inPort, Ethernet.MPLS_UNICAST, dstMac, treatment, filterObjective);
+    }
+
+    private FlowRule createFwdClassifierRule(PortNumber inPort,
+                                             short ethType,
+                                             MacAddress dstMac,
+                                             TrafficTreatment treatment,
+                                             FilteringObjective filterObjective) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort)
+                .matchEthDst(dstMac)
+                .matchEthType(ethType);
+
+        return DefaultFlowRule.builder()
+                .withSelector(selector.build())
+                .withTreatment(treatment)
+                .fromApp(filterObjective.appId())
+                .withPriority(filterObjective.priority())
+                .forDevice(deviceId)
+                .makePermanent()
+                .forTable(FabricConstants.TBL_FWD_CLASSIFIER_ID)
+                .build();
+    }
+
+    private TrafficTreatment createFwdClassifierTreatment(byte fwdType) {
+        PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_FWD_TYPE_ID,
+                                                ImmutableByteSequence.copyFrom(fwdType));
+        PiAction action = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_FORWARDING_TYPE_ID)
+                .withParameter(param)
+                .build();
+        return DefaultTrafficTreatment.builder()
+                .piTableAction(action)
+                .build();
+
+    }
+}
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();
+    }
+
+}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
new file mode 100644
index 0000000..7487fbe
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipeliner.java
@@ -0,0 +1,159 @@
+/*
+ * 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.ImmutableMap;
+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.PiCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flowobjective.NextObjective;
+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.Map;
+
+import static org.onosproject.pipelines.fabric.pipeliner.FabricPipeliner.fail;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handling next objective for fabric pipeliner.
+ */
+public class FabricNextPipeliner {
+    private static final Logger log = getLogger(FabricNextPipeliner.class);
+
+    // Next types
+    private static final byte TBL_SIMPLE = 0;
+    private static final byte TBL_HASHED = 1;
+    private static final byte TBL_BROADCAST = 2;
+    private static final byte PUNT = 3;
+    private static final Map<NextObjective.Type, Byte> NEXT_TYPE_MAP =
+            ImmutableMap.<NextObjective.Type, Byte>builder()
+                    .put(NextObjective.Type.SIMPLE, TBL_SIMPLE)
+                    .put(NextObjective.Type.HASHED, TBL_HASHED)
+                    .put(NextObjective.Type.BROADCAST, TBL_BROADCAST)
+                    .build();
+
+    protected DeviceId deviceId;
+
+    public FabricNextPipeliner(DeviceId deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public PipelinerTranslationResult next(NextObjective nextObjective) {
+        PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
+        FlowRule nextIdMappingRule = processNextIdMapping(nextObjective);
+        FlowRule nextRule = null;
+        switch (nextObjective.type()) {
+            case SIMPLE:
+                nextRule = processSimpleNext(nextObjective);
+                break;
+            default:
+                log.warn("Unsupported next type {}", nextObjective);
+                resultBuilder.setError(ObjectiveError.UNSUPPORTED);
+                break;
+        }
+
+        if (nextIdMappingRule != null && nextRule != null) {
+            resultBuilder.addFlowRule(nextIdMappingRule);
+            resultBuilder.addFlowRule(nextRule);
+        }
+
+        return resultBuilder.build();
+    }
+
+    private FlowRule processNextIdMapping(NextObjective next) {
+        // program the next id mapping table
+        TrafficSelector nextIdSelector = buildNextIdSelector(next.id());
+        TrafficTreatment setNextTypeTreatment = buildSetNextTypeTreatment(next.type());
+
+        return DefaultFlowRule.builder()
+                .withSelector(nextIdSelector)
+                .withTreatment(setNextTypeTreatment)
+                .forDevice(deviceId)
+                .forTable(FabricConstants.TBL_NEXT_ID_MAPPING_ID)
+                .makePermanent()
+                .withPriority(next.priority())
+                .fromApp(next.appId())
+                .build();
+    }
+
+    private FlowRule processSimpleNext(NextObjective next) {
+        if (next.next().size() > 1) {
+            log.warn("Only one treatment in simple next objective");
+            fail(next, ObjectiveError.BADPARAMS);
+            return null;
+        }
+
+        TrafficSelector selector = buildNextIdSelector(next.id());
+        TrafficTreatment treatment = next.next().iterator().next();
+        OutputInstruction outputInst = treatment.allInstructions()
+                .stream()
+                .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
+                .map(inst -> (OutputInstruction) inst)
+                .findFirst()
+                .orElse(null);
+
+        if (outputInst == null) {
+            log.warn("At least one output instruction in simple next objective");
+            fail(next, ObjectiveError.BADPARAMS);
+            return null;
+        }
+        return DefaultFlowRule.builder()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .forTable(FabricConstants.TBL_SIMPLE_ID)
+                .makePermanent()
+                .withPriority(next.priority())
+                .forDevice(deviceId)
+                .fromApp(next.appId())
+                .build();
+    }
+
+    private TrafficSelector buildNextIdSelector(int nextId) {
+        byte[] nextIdVal = new byte[]{(byte) nextId};
+        PiCriterion nextIdCrriterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, nextIdVal)
+                .build();
+        return DefaultTrafficSelector.builder()
+                .matchPi(nextIdCrriterion)
+                .build();
+    }
+
+    private TrafficTreatment buildSetNextTypeTreatment(NextObjective.Type nextType) {
+        byte nextTypeVal = NEXT_TYPE_MAP.getOrDefault(nextType, PUNT);
+        PiActionParam nextTypeParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_TYPE_ID,
+                                                        ImmutableByteSequence.copyFrom(nextTypeVal));
+        PiAction nextTypeAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_NEXT_TYPE_ID)
+                .withParameter(nextTypeParam)
+                .build();
+        return DefaultTrafficTreatment.builder()
+                .piTableAction(nextTypeAction)
+                .build();
+    }
+}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
new file mode 100644
index 0000000..e2d8048
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipeliner.java
@@ -0,0 +1,303 @@
+/*
+ * 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.ImmutableList;
+import com.google.common.collect.Lists;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Pipeliner for fabric pipeline.
+ */
+public class FabricPipeliner  extends AbstractHandlerBehaviour implements Pipeliner {
+    private static final Logger log = getLogger(FabricPipeliner.class);
+
+    protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
+            .register(KryoNamespaces.API)
+            .register(FabricNextGroup.class)
+            .build("FabricPipeliner");
+
+    // TODO: make this configurable
+    private static final long DEFAULT_INSTALLATION_TIME_OUT = 10;
+
+    protected DeviceId deviceId;
+    protected FlowRuleService flowRuleService;
+    protected GroupService groupService;
+    protected FlowObjectiveStore flowObjectiveStore;
+    protected FabricFilteringPipeliner pipelinerFilter;
+    protected FabricForwardingPipeliner pipelinerForward;
+    protected FabricNextPipeliner pipelinerNext;
+
+
+    @Override
+    public void init(DeviceId deviceId, PipelinerContext context) {
+        this.deviceId = deviceId;
+        this.flowRuleService = context.directory().get(FlowRuleService.class);
+        this.groupService = context.directory().get(GroupService.class);
+        this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
+        this.pipelinerFilter = new FabricFilteringPipeliner(deviceId);
+        this.pipelinerForward = new FabricForwardingPipeliner(deviceId);
+        this.pipelinerNext = new FabricNextPipeliner(deviceId);
+    }
+
+    @Override
+    public void filter(FilteringObjective filterObjective) {
+        PipelinerTranslationResult result = pipelinerFilter.filter(filterObjective);
+        if (result.error().isPresent()) {
+            fail(filterObjective, result.error().get());
+            return;
+        }
+
+        applyTranslationResult(filterObjective, result, success -> {
+            if (success) {
+                success(filterObjective);
+            } else {
+                fail(filterObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
+            }
+        });
+    }
+
+    @Override
+    public void forward(ForwardingObjective forwardObjective) {
+        PipelinerTranslationResult result = pipelinerForward.forward(forwardObjective);
+        if (result.error().isPresent()) {
+            fail(forwardObjective, result.error().get());
+            return;
+        }
+
+        applyTranslationResult(forwardObjective, result, success -> {
+            if (success) {
+                success(forwardObjective);
+            } else {
+                fail(forwardObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
+            }
+        });
+    }
+
+    @Override
+    public void next(NextObjective nextObjective) {
+        PipelinerTranslationResult result = pipelinerNext.next(nextObjective);
+
+        if (result.error().isPresent()) {
+            fail(nextObjective, result.error().get());
+            return;
+        }
+
+        applyTranslationResult(nextObjective, result, success -> {
+            if (!success) {
+                fail(nextObjective, ObjectiveError.GROUPINSTALLATIONFAILED);
+                return;
+            }
+
+            // Success, put next group to objective store
+            List<PortNumber> portNumbers = Lists.newArrayList();
+            nextObjective.next().forEach(treatment -> {
+                Instructions.OutputInstruction outputInst = treatment.allInstructions()
+                        .stream()
+                        .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
+                        .map(inst -> (Instructions.OutputInstruction) inst)
+                        .findFirst()
+                        .orElse(null);
+
+                if (outputInst != null) {
+                    portNumbers.add(outputInst.port());
+                }
+            });
+            FabricNextGroup nextGroup = new FabricNextGroup(nextObjective.type(),
+                                                            portNumbers);
+            flowObjectiveStore.putNextGroup(nextObjective.id(), nextGroup);
+            success(nextObjective);
+        });
+    }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        return null;
+    }
+
+    private void applyTranslationResult(Objective objective,
+                                        PipelinerTranslationResult result,
+                                        Consumer<Boolean> callback) {
+        Collection<GroupDescription> groups = result.groups();
+        Collection<FlowRule> flowRules = result.flowRules();
+        CompletableFuture.supplyAsync(() -> installGroups(objective, groups))
+                .thenApplyAsync(groupSuccess -> groupSuccess && installFlows(objective, flowRules))
+                .thenAcceptAsync(callback)
+                .exceptionally((ex) -> {
+                    log.warn("Got unexpected exception while applying translation result {}",
+                             result);
+                    fail(objective, ObjectiveError.UNKNOWN);
+                    return null;
+                });
+    }
+
+    private boolean installFlows(Objective objective, Collection<FlowRule> flowRules) {
+        if (flowRules.isEmpty()) {
+            return true;
+        }
+        CompletableFuture<Boolean> flowInstallFuture = new CompletableFuture<>();
+        FlowRuleOperationsContext ctx = new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                flowInstallFuture.complete(true);
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.warn("Failed to install flow rules: {}", flowRules);
+                flowInstallFuture.complete(false);
+            }
+        };
+
+        FlowRuleOperations ops = buildFlowRuleOps(objective, flowRules, ctx);
+        flowRuleService.apply(ops);
+
+        try {
+            return flowInstallFuture.get(DEFAULT_INSTALLATION_TIME_OUT, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.warn("Got exception while installing groups: {}", e);
+            return false;
+        }
+    }
+
+    private boolean installGroups(Objective objective, Collection<GroupDescription> groups) {
+        if (groups.isEmpty()) {
+            return true;
+        }
+        int numGroupsToBeInstalled = groups.size();
+        CompletableFuture<Boolean> groupInstallFuture = new CompletableFuture<>();
+        AtomicInteger numGroupsInstalled = new AtomicInteger(0);
+        GroupListener listener = new GroupListener() {
+            @Override
+            public void event(GroupEvent event) {
+                int currentNumGroupInstalled = numGroupsInstalled.incrementAndGet();
+                if (currentNumGroupInstalled == numGroupsToBeInstalled) {
+                    // install completed
+                    groupService.removeListener(this);
+                    groupInstallFuture.complete(true);
+                }
+            }
+            @Override
+            public boolean isRelevant(GroupEvent event) {
+                return groups.contains(event.subject());
+            }
+        };
+        groupService.addListener(listener);
+
+        switch (objective.op()) {
+            case ADD:
+                groups.forEach(groupService::addGroup);
+                break;
+            case REMOVE:
+                groups.forEach(group -> groupService.removeGroup(deviceId, group.appCookie(), objective.appId()));
+                break;
+            default:
+                log.warn("Unsupported objective operation {}", objective.op());
+                groupService.removeListener(listener);
+        }
+        try {
+            return groupInstallFuture.get(DEFAULT_INSTALLATION_TIME_OUT, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.warn("Got exception while installing groups: {}", e);
+            return false;
+        }
+    }
+
+    static void fail(Objective objective, ObjectiveError error) {
+        objective.context().ifPresent(ctx -> ctx.onError(objective, error));
+    }
+
+    static void success(Objective objective) {
+        objective.context().ifPresent(ctx -> ctx.onSuccess(objective));
+    }
+
+    static FlowRuleOperations buildFlowRuleOps(Objective objective, Collection<FlowRule> flowRules,
+                                               FlowRuleOperationsContext ctx) {
+        FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+        switch (objective.op()) {
+            case ADD:
+                flowRules.forEach(ops::add);
+                break;
+            case REMOVE:
+                flowRules.forEach(ops::remove);
+                break;
+            default:
+                log.warn("Unsupported op {} for {}", objective);
+                fail(objective, ObjectiveError.BADPARAMS);
+                return null;
+        }
+        return ops.build(ctx);
+    }
+
+    class FabricNextGroup implements NextGroup {
+        private NextObjective.Type type;
+        private Collection<PortNumber> outputPorts;
+
+        public FabricNextGroup(NextObjective.Type type, Collection<PortNumber> outputPorts) {
+            this.type = type;
+            this.outputPorts = ImmutableList.copyOf(outputPorts);
+        }
+
+        public NextObjective.Type type() {
+            return type;
+        }
+
+        public Collection<PortNumber> outputPorts() {
+            return outputPorts;
+        }
+
+        @Override
+        public byte[] data() {
+            return KRYO.serialize(this);
+        }
+    }
+}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java
new file mode 100644
index 0000000..8d8de54
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.Map;
+import java.util.Set;
+
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_TYPE;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV6_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_BOS;
+import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_LABEL;
+import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
+
+public enum ForwardingFunctionType {
+    /**
+     * L2 unicast, with vlan id + mac address criterion.
+     */
+    L2_UNICAST,
+
+    /**
+     * L2 broadcast, with vlan id criterion only.
+     */
+    L2_BROADCAST,
+
+    /**
+     * IPv4 unicast, with EtherType and IPv4 unicast destination address.
+     */
+    IPV4_UNICAST,
+
+    /**
+     * IPv4 multicast, with EtherType and IPv4 multicast destination address.
+     */
+    IPV4_MULTICAST,
+
+    /**
+     * IPv6 unicast, with EtherType and IPv6 unicast destination address.
+     */
+    IPV6_UNICAST,
+
+    /**
+     * IPv6 multicast, with EtherType and IPv6 multicast destination address.
+     */
+    IPV6_MULTICAST,
+
+    /**
+     * MPLS, with EtherType, MPLS label and MPLS BOS criterion.
+     */
+    MPLS,
+
+    /**
+     * Unsupported type.
+     */
+    UNSUPPORTED;
+
+    // Different criteria combinations for different FFT
+    private static final Set<Criterion.Type> L2_UNI_CRITERIA_TYPE =
+            ImmutableSet.of(VLAN_VID, ETH_DST);
+    private static final Set<Criterion.Type> L2_BRC_CRITERIA_TYPE =
+            ImmutableSet.of(VLAN_VID);
+    private static final Set<Criterion.Type> IPV4_UNI_CRITERIA_TYPE =
+            ImmutableSet.of(ETH_TYPE, IPV4_DST);
+    private static final Set<Criterion.Type> IPV4_MCAST_CRITERIA_TYPE =
+            ImmutableSet.of(ETH_TYPE, VLAN_VID, IPV4_DST);
+    private static final Set<Criterion.Type> IPV6_UNI_CRITERIA_TYPE =
+            ImmutableSet.of(ETH_TYPE, IPV6_DST);
+    private static final Set<Criterion.Type> IPV6_MCAST_CRITERIA_TYPE =
+            ImmutableSet.of(ETH_TYPE, VLAN_VID, IPV6_DST);
+    private static final Set<Criterion.Type> MPLS_UNI_CRITERIA_TYPE =
+            ImmutableSet.of(ETH_TYPE, MPLS_LABEL, MPLS_BOS);
+
+    private static final Map<Set<Criterion.Type>, ForwardingFunctionType> FFT_MAP =
+            ImmutableMap.<Set<Criterion.Type>, ForwardingFunctionType>builder()
+                    .put(L2_UNI_CRITERIA_TYPE, L2_UNICAST)
+                    .put(L2_BRC_CRITERIA_TYPE, L2_BROADCAST)
+                    .put(IPV4_UNI_CRITERIA_TYPE, IPV4_UNICAST)
+                    .put(IPV4_MCAST_CRITERIA_TYPE, IPV4_MULTICAST)
+                    .put(IPV6_UNI_CRITERIA_TYPE, IPV6_UNICAST)
+                    .put(IPV6_MCAST_CRITERIA_TYPE, IPV6_MULTICAST)
+                    .put(MPLS_UNI_CRITERIA_TYPE, MPLS)
+                    .build();
+
+    /**
+     * Gets forwarding function type of the forwarding objective.
+     *
+     * @param fwd the forwarding objective
+     * @return forwarding function type of the forwarding objective
+     */
+    public static ForwardingFunctionType getForwardingFunctionType(ForwardingObjective fwd) {
+        Set<Criterion.Type> criteriaType = Sets.newHashSet();
+        fwd.selector().criteria().stream().map(Criterion::type)
+                .forEach(criteriaType::add);
+
+        if (fwd.meta() != null) {
+            fwd.meta().criteria().stream().map(Criterion::type)
+                    .forEach(criteriaType::add);
+        }
+
+        return FFT_MAP.getOrDefault(criteriaType, UNSUPPORTED);
+    }
+}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/PipelinerTranslationResult.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/PipelinerTranslationResult.java
new file mode 100644
index 0000000..575d590
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/PipelinerTranslationResult.java
@@ -0,0 +1,154 @@
+/*
+ * 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.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.GroupDescription;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Translation results from fabric pipeliner.
+ */
+public final class PipelinerTranslationResult {
+    private Collection<FlowRule> flowRules;
+    private Collection<GroupDescription> groups;
+    private ObjectiveError error;
+
+    private PipelinerTranslationResult(Collection<FlowRule> flowRules,
+                                      Collection<GroupDescription> groups,
+                                      ObjectiveError error) {
+        this.flowRules = flowRules;
+        this.groups = groups;
+        this.error = error;
+    }
+
+    /**
+     * Gets flow rules from result.
+     *
+     * @return flow rules
+     */
+    public Collection<FlowRule> flowRules() {
+        return flowRules;
+    }
+
+    /**
+     * Gets groups from result.
+     *
+     * @return groups
+     */
+    public Collection<GroupDescription> groups() {
+        return groups;
+    }
+
+    /**
+     * Gets error from result.
+     *
+     * @return error of the result; empty if there is no error
+     */
+    public Optional<ObjectiveError> error() {
+        return Optional.ofNullable(error);
+    }
+
+    /**
+     * Creates a new builder.
+     *
+     * @return the builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("flowRules", flowRules)
+                .add("groups", groups)
+                .add("error", error)
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(flowRules, groups, error);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final PipelinerTranslationResult other = (PipelinerTranslationResult) obj;
+        return Objects.equals(this.flowRules, other.flowRules)
+                && Objects.equals(this.groups, other.groups)
+                && Objects.equals(this.error, other.error);
+    }
+
+    /**
+     * Builder for PipelinerTranslationResult.
+     */
+    public static final class Builder {
+        private ImmutableList.Builder<FlowRule> flowRules = ImmutableList.builder();
+        private ImmutableList.Builder<GroupDescription> groups = ImmutableList.builder();
+        private ObjectiveError error = null;
+
+        // Hide default constructor
+        private Builder() {
+        }
+
+        /**
+         * Adds flow rule to the result.
+         *
+         * @param flowRule the flow rule
+         */
+        public void addFlowRule(FlowRule flowRule) {
+            flowRules.add(flowRule);
+        }
+
+        /**
+         * Adds group to the result.
+         *
+         * @param group the group
+         */
+        public void addGroup(GroupDescription group) {
+            groups.add(group);
+        }
+
+        /**
+         * Sets objective error to the result.
+         *
+         * @param error the error
+         */
+        public void setError(ObjectiveError error) {
+            this.error = error;
+        }
+
+        public PipelinerTranslationResult build() {
+            return new PipelinerTranslationResult(flowRules.build(),
+                                                  groups.build(),
+                                                  error);
+        }
+    }
+}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/package-info.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/package-info.java
new file mode 100644
index 0000000..75bf40e
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Pipeliner for fabric.p4.
+ */
+package org.onosproject.pipelines.fabric.pipeliner;
\ No newline at end of file