ONOS-894 : Implemented GroupProvider

Change-Id: I2755abd433921a420ad545fcf6fef61e90a8b89f
diff --git a/features/features.xml b/features/features.xml
index a456d2c..de1f0a5 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -149,7 +149,7 @@
         <bundle>mvn:org.onosproject/onos-of-provider-device/@ONOS-VERSION</bundle>
         <bundle>mvn:org.onosproject/onos-of-provider-packet/@ONOS-VERSION</bundle>
         <bundle>mvn:org.onosproject/onos-of-provider-flow/@ONOS-VERSION</bundle>
-
+        <bundle>mvn:org.onosproject/onos-of-provider-group/@ONOS-VERSION</bundle>
     </feature>
 
     <feature name="onos-app-tvue" version="@FEATURE-VERSION"
diff --git a/providers/openflow/group/pom.xml b/providers/openflow/group/pom.xml
new file mode 100644
index 0000000..32ca37f
--- /dev/null
+++ b/providers/openflow/group/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-of-providers</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-of-provider-group</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS OpenFlow protocol group provider</description>
+
+</project>
\ No newline at end of file
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java
new file mode 100644
index 0000000..8ff8e70
--- /dev/null
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2015 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.provider.of.group.impl;
+
+import com.google.common.collect.Lists;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
+import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlIn;
+import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlOut;
+import org.projectfloodlight.openflow.protocol.action.OFActionDecMplsTtl;
+import org.projectfloodlight.openflow.protocol.action.OFActionDecNwTtl;
+import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
+import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Builder for GroupBucketEntry.
+ */
+public class GroupBucketEntryBuilder {
+
+    private List<OFBucket> ofBuckets;
+    private OFGroupType type;
+
+    private final Logger log = getLogger(getClass());
+
+    /**
+     * Creates a builder.
+     *
+     * @param ofBuckets list of OFBucket
+     * @param type Group type
+     */
+    public GroupBucketEntryBuilder(List<OFBucket> ofBuckets, OFGroupType type) {
+        this.ofBuckets = ofBuckets;
+        this.type = type;
+    }
+
+    /**
+     * Builds a GroupBuckets.
+     *
+     * @return GroupBuckets object, a list of GroupBuck
+     */
+    public GroupBuckets build() {
+        List<GroupBucket> bucketList = Lists.newArrayList();
+
+        for (OFBucket bucket: ofBuckets) {
+            TrafficTreatment treatment = buildTreatment(bucket.getActions());
+            // TODO: Use GroupBucketEntry
+            GroupBucket groupBucket = null;
+            switch (type) {
+                case INDIRECT:
+                    groupBucket =
+                            DefaultGroupBucket.createIndirectGroupBucket(treatment);
+                    break;
+                case SELECT:
+                    groupBucket =
+                            DefaultGroupBucket.createSelectGroupBucket(treatment);
+                    break;
+                case FF:
+                    PortNumber port =
+                            PortNumber.portNumber(bucket.getWatchPort().getPortNumber());
+                    GroupId groupId =
+                            new DefaultGroupId(bucket.getWatchGroup().getGroupNumber());
+                    groupBucket =
+                            DefaultGroupBucket.createFailoverGroupBucket(treatment,
+                                    port, groupId);
+                    break;
+                default:
+                    log.error("Unsupported Group type : {}", type);
+            }
+            if (groupBucket != null) {
+                bucketList.add(groupBucket);
+            }
+        }
+        return new GroupBuckets(bucketList);
+    }
+
+
+    private TrafficTreatment buildTreatment(List<OFAction> actions) {
+        TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+        // If this is a drop rule
+        if (actions.size() == 0) {
+            builder.drop();
+            return builder.build();
+        }
+        for (OFAction act : actions) {
+            switch (act.getType()) {
+                case OUTPUT:
+                    OFActionOutput out = (OFActionOutput) act;
+                    builder.setOutput(
+                            PortNumber.portNumber(out.getPort().getPortNumber()));
+                    break;
+                case SET_VLAN_VID:
+                    OFActionSetVlanVid vlan = (OFActionSetVlanVid) act;
+                    builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan()));
+                    break;
+                case SET_VLAN_PCP:
+                    OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act;
+                    builder.setVlanPcp(pcp.getVlanPcp().getValue());
+                    break;
+                case SET_DL_DST:
+                    OFActionSetDlDst dldst = (OFActionSetDlDst) act;
+                    builder.setEthDst(
+                            MacAddress.valueOf(dldst.getDlAddr().getLong()));
+                    break;
+                case SET_DL_SRC:
+                    OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act;
+                    builder.setEthSrc(
+                            MacAddress.valueOf(dlsrc.getDlAddr().getLong()));
+
+                    break;
+                case SET_NW_DST:
+                    OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
+                    IPv4Address di = nwdst.getNwAddr();
+                    builder.setIpDst(Ip4Address.valueOf(di.getInt()));
+                    break;
+                case SET_NW_SRC:
+                    OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
+                    IPv4Address si = nwsrc.getNwAddr();
+                    builder.setIpSrc(Ip4Address.valueOf(si.getInt()));
+                    break;
+                case EXPERIMENTER:
+                    OFActionExperimenter exp = (OFActionExperimenter) act;
+                    if (exp.getExperimenter() == 0x80005A06 ||
+                            exp.getExperimenter() == 0x748771) {
+                        OFActionCircuit ct = (OFActionCircuit) exp;
+                        builder.setLambda(((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber());
+                    } else {
+                        log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
+                    }
+                    break;
+                case SET_FIELD:
+                    OFActionSetField setField = (OFActionSetField) act;
+                    handleSetField(builder, setField.getField());
+                    break;
+                case POP_MPLS:
+                    OFActionPopMpls popMpls = (OFActionPopMpls) act;
+                    builder.popMpls((short) popMpls.getEthertype().getValue());
+                    break;
+                case PUSH_MPLS:
+                    OFActionPushMpls pushMpls = (OFActionPushMpls) act;
+                    builder.pushMpls();
+                    break;
+                case COPY_TTL_IN:
+                    OFActionCopyTtlIn copyTtlIn = (OFActionCopyTtlIn) act;
+                    builder.copyTtlIn();
+                    break;
+                case COPY_TTL_OUT:
+                    OFActionCopyTtlOut copyTtlOut = (OFActionCopyTtlOut) act;
+                    builder.copyTtlOut();
+                    break;
+                case DEC_MPLS_TTL:
+                    OFActionDecMplsTtl decMplsTtl = (OFActionDecMplsTtl) act;
+                    builder.decMplsTtl();
+                    break;
+                case DEC_NW_TTL:
+                    OFActionDecNwTtl decNwTtl = (OFActionDecNwTtl) act;
+                    builder.decNwTtl();
+                    break;
+                case SET_TP_DST:
+                case SET_TP_SRC:
+                case POP_PBB:
+                case POP_VLAN:
+                case PUSH_PBB:
+                case PUSH_VLAN:
+                case SET_MPLS_LABEL:
+                case SET_MPLS_TC:
+                case SET_MPLS_TTL:
+                case SET_NW_ECN:
+                case SET_NW_TOS:
+                case SET_NW_TTL:
+                case SET_QUEUE:
+                case STRIP_VLAN:
+                case ENQUEUE:
+
+                case GROUP:
+                default:
+                    log.warn("Action type {} not yet implemented.", act.getType());
+            }
+        }
+
+        return builder.build();
+    }
+
+    private void handleSetField(TrafficTreatment.Builder builder, OFOxm<?> oxm) {
+        switch (oxm.getMatchField().id) {
+            case VLAN_PCP:
+                @SuppressWarnings("unchecked")
+                OFOxm<VlanPcp> vlanpcp = (OFOxm<VlanPcp>) oxm;
+                builder.setVlanPcp(vlanpcp.getValue().getValue());
+                break;
+            case VLAN_VID:
+                @SuppressWarnings("unchecked")
+                OFOxm<OFVlanVidMatch> vlanvid = (OFOxm<OFVlanVidMatch>) oxm;
+                builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan()));
+                break;
+            case ETH_DST:
+                @SuppressWarnings("unchecked")
+                OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethdst =
+                        (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+                builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong()));
+                break;
+            case ETH_SRC:
+                @SuppressWarnings("unchecked")
+                OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethsrc =
+                        (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+                builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong()));
+                break;
+            case IPV4_DST:
+                @SuppressWarnings("unchecked")
+                OFOxm<IPv4Address> ip4dst = (OFOxm<IPv4Address>) oxm;
+                builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt()));
+                break;
+            case IPV4_SRC:
+                @SuppressWarnings("unchecked")
+                OFOxm<IPv4Address> ip4src = (OFOxm<IPv4Address>) oxm;
+                builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt()));
+                break;
+            case MPLS_LABEL:
+                @SuppressWarnings("unchecked")
+                OFOxm<U32> labelId = (OFOxm<U32>) oxm;
+                builder.setMpls((int) labelId.getValue().getValue());
+                break;
+            case ARP_OP:
+            case ARP_SHA:
+            case ARP_SPA:
+            case ARP_THA:
+            case ARP_TPA:
+            case BSN_EGR_PORT_GROUP_ID:
+            case BSN_GLOBAL_VRF_ALLOWED:
+            case BSN_IN_PORTS_128:
+            case BSN_L3_DST_CLASS_ID:
+            case BSN_L3_INTERFACE_CLASS_ID:
+            case BSN_L3_SRC_CLASS_ID:
+            case BSN_LAG_ID:
+            case BSN_TCP_FLAGS:
+            case BSN_UDF0:
+            case BSN_UDF1:
+            case BSN_UDF2:
+            case BSN_UDF3:
+            case BSN_UDF4:
+            case BSN_UDF5:
+            case BSN_UDF6:
+            case BSN_UDF7:
+            case BSN_VLAN_XLATE_PORT_GROUP_ID:
+            case BSN_VRF:
+            case ETH_TYPE:
+            case ICMPV4_CODE:
+            case ICMPV4_TYPE:
+            case ICMPV6_CODE:
+            case ICMPV6_TYPE:
+            case IN_PHY_PORT:
+            case IN_PORT:
+            case IPV6_DST:
+            case IPV6_FLABEL:
+            case IPV6_ND_SLL:
+            case IPV6_ND_TARGET:
+            case IPV6_ND_TLL:
+            case IPV6_SRC:
+            case IP_DSCP:
+            case IP_ECN:
+            case IP_PROTO:
+            case METADATA:
+            case MPLS_TC:
+            case OCH_SIGID:
+            case OCH_SIGID_BASIC:
+            case OCH_SIGTYPE:
+            case OCH_SIGTYPE_BASIC:
+            case SCTP_DST:
+            case SCTP_SRC:
+            case TCP_DST:
+            case TCP_SRC:
+            case TUNNEL_ID:
+            case UDP_DST:
+            case UDP_SRC:
+            default:
+                log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id);
+                break;
+        }
+    }
+}
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
new file mode 100644
index 0000000..eac5f26
--- /dev/null
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2015 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.provider.of.group.impl;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFGroupAdd;
+import org.projectfloodlight.openflow.protocol.OFGroupDelete;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Builder for GroupMod.
+ */
+public final class GroupModBuilder {
+
+    private GroupBuckets buckets;
+    private GroupId groupId;
+    private GroupDescription.Type type;
+    private OFFactory factory;
+    private Long xid;
+
+    private final Logger log = getLogger(getClass());
+
+    private static final int OFPCML_NO_BUFFER = 0xffff;
+
+    private GroupModBuilder(GroupBuckets buckets, GroupId groupId,
+                             GroupDescription.Type type, OFFactory factory,
+                             Optional<Long> xid) {
+        this.buckets = buckets;
+        this.groupId = groupId;
+        this.type = type;
+        this.factory = factory;
+        this.xid = xid.orElse((long) 0);
+    }
+
+    /**
+     * Creates a builder for GroupMod.
+     *
+     * @param buckets GroupBuckets object
+     * @param groupId Group Id to create
+     * @param type Group type
+     * @param factory OFFactory object
+     * @param xid transaction ID
+     * @return GroupModBuilder object
+     */
+    public static GroupModBuilder builder(GroupBuckets buckets, GroupId groupId,
+                                          GroupDescription.Type type, OFFactory factory,
+                                          Optional<Long> xid) {
+
+        return new GroupModBuilder(buckets, groupId, type, factory, xid);
+    }
+
+    /**
+     * Builds the GroupAdd OF message.
+     *
+     * @return GroupAdd OF message
+     */
+    public OFGroupAdd buildGroupAdd() {
+
+        List<OFBucket> ofBuckets = new ArrayList<OFBucket>();
+        for (GroupBucket bucket: buckets.buckets()) {
+            List<OFAction> actions = buildActions(bucket.treatment());
+
+            OFBucket.Builder bucketBuilder = factory.buildBucket();
+            bucketBuilder.setActions(actions);
+            if (type == GroupDescription.Type.SELECT) {
+                bucketBuilder.setWeight(1);
+            }
+            bucketBuilder.setWatchGroup(OFGroup.ANY);
+            bucketBuilder.setWatchPort(OFPort.ANY);
+            OFBucket ofBucket = bucketBuilder.build();
+            ofBuckets.add(ofBucket);
+        }
+
+        OFGroupAdd groupMsg = factory.buildGroupAdd()
+                .setGroup(OFGroup.of(groupId.id()))
+                .setBuckets(ofBuckets)
+                .setGroupType(getOFGroupType(type))
+                .setXid(xid)
+                .build();
+
+        return groupMsg;
+    }
+
+    /**
+     * Builds the GroupMod OF message.
+     *
+     * @return GroupMod OF message
+     */
+    public OFGroupMod buildGroupMod() {
+        List<OFBucket> ofBuckets = new ArrayList<OFBucket>();
+        for (GroupBucket bucket: buckets.buckets()) {
+            List<OFAction> actions = buildActions(bucket.treatment());
+
+            OFBucket.Builder bucketBuilder = factory.buildBucket();
+            bucketBuilder.setActions(actions);
+            if (type == GroupDescription.Type.SELECT) {
+                bucketBuilder.setWeight(1);
+            }
+            bucketBuilder.setWatchGroup(OFGroup.ANY);
+            bucketBuilder.setWatchPort(OFPort.ANY);
+            OFBucket ofBucket = bucketBuilder.build();
+            ofBuckets.add(ofBucket);
+        }
+
+        OFGroupMod groupMsg = factory.buildGroupModify()
+                .setGroup(OFGroup.of(groupId.id()))
+                .setBuckets(ofBuckets)
+                .setGroupType(getOFGroupType(type))
+                .setXid(xid)
+                .build();
+
+        return groupMsg;
+    }
+
+    /**
+     * Builds the GroupDel OF message.
+     *
+     * @return GroupDel OF message
+     */
+    public OFGroupDelete buildGroupDel() {
+
+        OFGroupDelete groupMsg = factory.buildGroupDelete()
+                .setGroup(OFGroup.of(groupId.id()))
+                .setGroupType(OFGroupType.SELECT)
+                .setXid(xid)
+                .build();
+
+        return groupMsg;
+    }
+
+    private List<OFAction> buildActions(TrafficTreatment treatment) {
+        List<OFAction> actions = new LinkedList<>();
+        if (treatment == null) {
+            return actions;
+        }
+        for (Instruction i : treatment.instructions()) {
+            switch (i.type()) {
+                case DROP:
+                    log.warn("Saw drop action; assigning drop action");
+                    return new LinkedList<>();
+                case L0MODIFICATION:
+                    actions.add(buildL0Modification(i));
+                    break;
+                case L2MODIFICATION:
+                    actions.add(buildL2Modification(i));
+                    break;
+                case L3MODIFICATION:
+                    actions.add(buildL3Modification(i));
+                    break;
+                case OUTPUT:
+                    Instructions.OutputInstruction out =
+                            (Instructions.OutputInstruction) i;
+                    OFActionOutput.Builder action = factory.actions().buildOutput()
+                            .setPort(OFPort.of((int) out.port().toLong()));
+                    if (out.port().equals(PortNumber.CONTROLLER)) {
+                        action.setMaxLen(OFPCML_NO_BUFFER);
+                    }
+                    actions.add(action.build());
+                    break;
+                case GROUP:
+                default:
+                    log.warn("Instruction type {} not yet implemented.", i.type());
+            }
+        }
+
+        return actions;
+    }
+
+    private OFAction buildL0Modification(Instruction i) {
+        L0ModificationInstruction l0m = (L0ModificationInstruction) i;
+        switch (l0m.subtype()) {
+            case LAMBDA:
+                L0ModificationInstruction.ModLambdaInstruction ml =
+                        (L0ModificationInstruction.ModLambdaInstruction) i;
+                return factory.actions().circuit(factory.oxms().ochSigidBasic(
+                        new CircuitSignalID((byte) 1, (byte) 2, ml.lambda(), (short) 1)));
+            default:
+                log.warn("Unimplemented action type {}.", l0m.subtype());
+                break;
+        }
+        return null;
+    }
+
+    private OFAction buildL2Modification(Instruction i) {
+        L2ModificationInstruction l2m = (L2ModificationInstruction) i;
+        L2ModificationInstruction.ModEtherInstruction eth;
+        OFOxm<?> oxm = null;
+        switch (l2m.subtype()) {
+            case ETH_DST:
+                eth = (L2ModificationInstruction.ModEtherInstruction) l2m;
+                oxm = factory.oxms().ethDst(MacAddress.of(eth.mac().toLong()));
+                break;
+            case ETH_SRC:
+                eth = (L2ModificationInstruction.ModEtherInstruction) l2m;
+                oxm = factory.oxms().ethSrc(MacAddress.of(eth.mac().toLong()));
+                break;
+            case VLAN_ID:
+                L2ModificationInstruction.ModVlanIdInstruction vlanId =
+                        (L2ModificationInstruction.ModVlanIdInstruction) l2m;
+                oxm = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort()));
+                break;
+            case VLAN_PCP:
+                L2ModificationInstruction.ModVlanPcpInstruction vlanPcp =
+                        (L2ModificationInstruction.ModVlanPcpInstruction) l2m;
+                oxm = factory.oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp()));
+                break;
+            case MPLS_PUSH:
+                L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions =
+                        (L2ModificationInstruction.PushHeaderInstructions) l2m;
+                return factory.actions().pushMpls(EthType.of(pushHeaderInstructions
+                        .ethernetType().getEtherType()));
+            case MPLS_POP:
+                L2ModificationInstruction.PushHeaderInstructions popHeaderInstructions =
+                        (L2ModificationInstruction.PushHeaderInstructions) l2m;
+                return factory.actions().popMpls(EthType.of(popHeaderInstructions
+                        .ethernetType().getEtherType()));
+            case MPLS_LABEL:
+                L2ModificationInstruction.ModMplsLabelInstruction mplsLabel =
+                        (L2ModificationInstruction.ModMplsLabelInstruction) l2m;
+                oxm = factory.oxms().mplsLabel(U32.of(mplsLabel.label()
+                        .longValue()));
+                break;
+            case DEC_MPLS_TTL:
+                return factory.actions().decMplsTtl();
+            default:
+                log.warn("Unimplemented action type {}.", l2m.subtype());
+                break;
+        }
+
+        if (oxm != null) {
+            return factory.actions().buildSetField().setField(oxm).build();
+        }
+        return null;
+    }
+
+    private OFAction buildL3Modification(Instruction i) {
+        L3ModificationInstruction l3m = (L3ModificationInstruction) i;
+        L3ModificationInstruction.ModIPInstruction ip;
+        Ip4Address ip4;
+        OFOxm<?> oxm = null;
+        switch (l3m.subtype()) {
+            case IP_DST:
+                ip = (L3ModificationInstruction.ModIPInstruction) i;
+                ip4 = ip.ip().getIp4Address();
+                oxm = factory.oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
+                break;
+            case IP_SRC:
+                ip = (L3ModificationInstruction.ModIPInstruction) i;
+                ip4 = ip.ip().getIp4Address();
+                oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+                break;
+            case DEC_TTL:
+                return factory.actions().decNwTtl();
+            case TTL_IN:
+                return factory.actions().copyTtlIn();
+            case TTL_OUT:
+                return factory.actions().copyTtlOut();
+            default:
+                log.warn("Unimplemented action type {}.", l3m.subtype());
+                break;
+        }
+
+        if (oxm != null) {
+            return factory.actions().buildSetField().setField(oxm).build();
+        }
+        return null;
+    }
+
+    private OFGroupType getOFGroupType(GroupDescription.Type groupType) {
+        switch (groupType) {
+            case INDIRECT:
+                return OFGroupType.INDIRECT;
+            case SELECT:
+                return OFGroupType.SELECT;
+            case FAILOVER:
+                return OFGroupType.FF;
+            case ALL:
+                return OFGroupType.ALL;
+            default:
+                log.error("Unsupported group type : {}", groupType);
+                break;
+        }
+        return null;
+    }
+}
+
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
new file mode 100644
index 0000000..90c700e
--- /dev/null
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2015 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.provider.of.group.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsRequest;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Sends Group Stats Request and collect the group statistics with a time interval.
+ */
+public class GroupStatsCollector implements TimerTask {
+
+    private final HashedWheelTimer timer = Timer.getTimer();
+    private final OpenFlowSwitch sw;
+    private final Logger log = getLogger(getClass());
+    private final int refreshInterval;
+
+    private Timeout timeout;
+    private final AtomicLong xidCounter = new AtomicLong(1);
+
+    private boolean stopTimer = false;
+
+    /**
+     * Creates a GroupStatsCollector object.
+     *
+     * @param sw Open Flow switch
+     * @param interval time interval for collecting group statistic
+     */
+    public GroupStatsCollector(OpenFlowSwitch sw, int interval) {
+        this.sw = sw;
+        this.refreshInterval = interval;
+    }
+
+    @Override
+    public void run(Timeout timeout) throws Exception {
+        log.trace("Collecting stats for {}", sw.getStringId());
+
+        sendGroupStatistic();
+
+        if (!this.stopTimer) {
+            log.trace("Scheduling stats collection in {} seconds for {}",
+                    this.refreshInterval, this.sw.getStringId());
+            timeout.getTimer().newTimeout(this, refreshInterval,
+                    TimeUnit.SECONDS);
+        }
+    }
+
+    private void sendGroupStatistic() {
+        if (log.isTraceEnabled()) {
+            log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole());
+        }
+        if (sw.getRole() != RoleState.MASTER) {
+            return;
+        }
+        Long statsXid = xidCounter.getAndAdd(2);
+        OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest()
+                .setGroup(OFGroup.ALL)
+                .setXid(statsXid)
+                .build();
+        sw.sendMsg(statsRequest);
+
+        Long descXid = statsXid + 1;
+        OFGroupDescStatsRequest descStatsRequest =
+                sw.factory().buildGroupDescStatsRequest()
+                        .setXid(descXid)
+                        .build();
+        sw.sendMsg(descStatsRequest);
+    }
+
+    /**
+     * Starts the collector.
+     */
+    public void start() {
+        log.info("Staring Group Stats collection thread for {}", sw.getStringId());
+        timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Stops the collector.
+     */
+    public void stop() {
+        log.info("Stopping Group Stats collection thread for {}", sw.getStringId());
+        this.stopTimer = true;
+        timeout.cancel();
+    }
+}
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
new file mode 100644
index 0000000..a477c39
--- /dev/null
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2015 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.provider.of.group.impl;
+
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProvider;
+import org.onosproject.net.group.GroupProviderRegistry;
+import org.onosproject.net.group.GroupProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to handle Group.
+ */
+@Component(immediate = true)
+public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenFlowController controller;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupProviderRegistry providerRegistry;
+
+    private GroupProviderService providerService;
+
+    static final int POLL_INTERVAL = 10;
+
+    private final InternalGroupProvider listener = new InternalGroupProvider();
+
+    private final AtomicLong xidCounter = new AtomicLong(1);
+    private final Map<Dpid, GroupStatsCollector> collectors = Maps.newHashMap();
+    private final Map<Long, OFStatsReply> groupStats = Maps.newHashMap();
+    private final Map<Long, GroupOperation> pendingGroupOperations =
+            Maps.newConcurrentMap();
+
+    /**
+     * Creates a OpenFlow group provider.
+     */
+    public OpenFlowGroupProvider() {
+        super(new ProviderId("of", "org.onosproject.provider.group"));
+    }
+
+    @Activate
+    public void activate() {
+        providerService = providerRegistry.register(this);
+        controller.addListener(listener);
+        controller.addEventListener(listener);
+
+        for (OpenFlowSwitch sw : controller.getSwitches()) {
+            GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
+            gsc.start();
+            collectors.put(new Dpid(sw.getId()), gsc);
+        }
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        providerRegistry.unregister(this);
+        providerService = null;
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
+        Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
+        final Dpid dpid = Dpid.dpid(deviceId.uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+        for (GroupOperation groupOperation: groupOps.operations()) {
+            if (sw == null) {
+                log.error("SW {} is not found", sw.getStringId());
+                return;
+            }
+            final Long groupModXid = xidCounter.getAndIncrement();
+            GroupModBuilder builder =
+                    GroupModBuilder.builder(groupOperation.buckets(),
+                            groupOperation.groupId(),
+                            groupOperation.groupType(),
+                            sw.factory(),
+                            Optional.of(groupModXid));
+            OFGroupMod groupMod = null;
+            switch (groupOperation.opType()) {
+                case ADD:
+                    groupMod = builder.buildGroupAdd();
+                    break;
+                case MODIFY:
+                    groupMod = builder.buildGroupMod();
+                    break;
+                case DELETE:
+                    groupMod = builder.buildGroupDel();
+                    break;
+                default:
+                    log.error("Unsupported Group operation");
+            }
+            sw.sendMsg(groupMod);
+            pendingGroupOperations.put(groupModXid, groupOperation);
+        }
+     }
+
+    private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
+        DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
+
+        OFGroupStatsReply groupStatsReply = null;
+        OFGroupDescStatsReply groupDescStatsReply = null;
+
+        if (statsReply.getStatsType() == OFStatsType.GROUP) {
+            OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
+            if (reply != null) {
+                groupStatsReply = (OFGroupStatsReply) statsReply;
+                groupDescStatsReply = (OFGroupDescStatsReply) reply;
+                groupStats.remove(statsReply.getXid() + 1);
+            } else {
+                groupStats.put(statsReply.getXid(), statsReply);
+            }
+        } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
+            OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
+            if (reply != null) {
+                groupStatsReply = (OFGroupStatsReply) reply;
+                groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
+                groupStats.remove(statsReply.getXid() - 1);
+            } else {
+                groupStats.put(statsReply.getXid(), statsReply);
+            }
+        }
+
+        if (groupStatsReply != null && groupDescStatsReply != null) {
+            Collection<Group> groups = buildGroupMetrics(deviceId,
+                    groupStatsReply, groupDescStatsReply);
+            providerService.pushGroupMetrics(deviceId, groups);
+            for (Group group: groups) {
+                pendingGroupOperations.remove(group.id());
+            }
+        }
+    }
+
+    private Collection<Group> buildGroupMetrics(DeviceId deviceId,
+                                   OFGroupStatsReply groupStatsReply,
+                                   OFGroupDescStatsReply groupDescStatsReply) {
+
+        Map<Integer, Group> groups = Maps.newHashMap();
+
+        for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) {
+            int id = entry.getGroup().getGroupNumber();
+            GroupId groupId = new DefaultGroupId(id);
+            GroupDescription.Type type = getGroupType(entry.getGroupType());
+            GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(),
+                    entry.getGroupType()).build();
+            DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets);
+            groups.put(id, group);
+        }
+
+        for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
+            int groupId = entry.getGroup().getGroupNumber();
+            DefaultGroup group = (DefaultGroup) groups.get(groupId);
+            if (group != null) {
+                group.setBytes(entry.getByteCount().getValue());
+                group.setLife(entry.getDurationSec());
+                group.setPackets(entry.getPacketCount().getValue());
+                group.setReferenceCount(entry.getRefCount());
+            }
+        }
+
+        return groups.values();
+    }
+
+    private GroupDescription.Type getGroupType(OFGroupType type) {
+        switch (type) {
+            case ALL:
+                return  GroupDescription.Type.ALL;
+            case INDIRECT:
+                return GroupDescription.Type.INDIRECT;
+            case SELECT:
+                return GroupDescription.Type.SELECT;
+            case FF:
+                return GroupDescription.Type.FAILOVER;
+            default:
+                log.error("Unsupported OF group type : {}", type);
+                break;
+        }
+        return null;
+    }
+
+    private class InternalGroupProvider
+            implements OpenFlowSwitchListener, OpenFlowEventListener {
+
+        @Override
+        public void handleMessage(Dpid dpid, OFMessage msg) {
+            switch (msg.getType()) {
+                case STATS_REPLY:
+                    pushGroupMetrics(dpid, (OFStatsReply) msg);
+                    break;
+                case ERROR:
+                    OFErrorMsg errorMsg = (OFErrorMsg) msg;
+                    if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) {
+                        GroupOperation operation =
+                                pendingGroupOperations.get(errorMsg.getXid());
+                        if (operation != null) {
+                            providerService.groupOperationFailed(operation);
+                            log.warn("received Error message {} from {}", msg, dpid);
+                        }
+                        break;
+                    }
+                default:
+                    log.debug("Unhandled message type: {}", msg.getType());
+            }
+        }
+
+        @Override
+        public void switchAdded(Dpid dpid) {
+            GroupStatsCollector gsc = new GroupStatsCollector(
+                    controller.getSwitch(dpid), POLL_INTERVAL);
+            gsc.start();
+            collectors.put(dpid, gsc);
+        }
+
+        @Override
+        public void switchRemoved(Dpid dpid) {
+            GroupStatsCollector collector = collectors.remove(dpid);
+            if (collector != null) {
+                collector.stop();
+            }
+        }
+
+        @Override
+        public void switchChanged(Dpid dpid) {
+        }
+
+        @Override
+        public void portChanged(Dpid dpid, OFPortStatus status) {
+        }
+
+        @Override
+        public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+        }
+    }
+
+}
diff --git a/providers/openflow/pom.xml b/providers/openflow/pom.xml
index 48ce6fe..4a733fd 100644
--- a/providers/openflow/pom.xml
+++ b/providers/openflow/pom.xml
@@ -37,6 +37,7 @@
         <module>host</module>
         <module>packet</module>
         <module>flow</module>
+        <module>group</module>
     </modules>
 
     <dependencies>