Driver API for creating user defined groups
diff --git a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
index ddc6c31..f5648af 100644
--- a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
@@ -146,7 +146,7 @@
      * Add the OFBucket to groups that have reachability through the given port.
      * This API can be used by applications, when a port is operational again,
      * to add that port to all the relevant groups
-     * 
+     *
      * @param port Port Number to be added to groups
      * @return None
      */
@@ -169,6 +169,20 @@
     public void createTunnel(String tunnelId, List<String> route, NeighborSet ns);
 
     /**
+     * Create a group chain with the same label stack for a given set of ports
+     * in the neighborset. This can be used for a basic scenario of tunnel based
+     * policy routing.
+     *
+     * @param labelStack list of router segment Ids to be pushed
+     * @param ns neighborSet to get to the first router in the labelStack. NOTE:
+     *        The edgeLabel inside the neighborSet is ignored and user should
+     *        explicitly push that label on to the labelStack that is passed as
+     *        first argument
+     * @return group identifier
+     */
+    public int createGroup(List<Integer> labelStack, List<PortNumber> ports);
+
+    /**
      * Remove all groups for the tunnel
      *
      * @param tunnelId tunnel ID to remove
@@ -176,6 +190,14 @@
     public void removeTunnel(String tunnelId);
 
     /**
+     * Remove the specified group
+     *
+     * @param groupId group identifier
+     * @return success/fail
+     */
+    public boolean removeGroup(int groupId);
+
+    /**
      * Return the first group ID for the tunnel.
      * If the router is not the source of the tunnel, it returns -1
      *
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
index fec88c5..bd55d83 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -146,6 +146,7 @@
     private List<Integer> segmentIds;
     private boolean isEdgeRouter;
     private ConcurrentMap<NeighborSet, EcmpInfo> ecmpGroups;
+    private ConcurrentMap<Integer, EcmpInfo> userDefinedGroups;
     private ConcurrentMap<String, List<Integer>> tunnelGroupIdTable;
     private ConcurrentMap<PortNumber, ArrayList<NeighborSet>> portNeighborSetMap;
     private AtomicInteger groupid;
@@ -160,6 +161,7 @@
         neighbors = new ConcurrentHashMap<Dpid, Set<PortNumber>>();
         portToNeighbors = new ConcurrentHashMap<PortNumber, Dpid>();
         ecmpGroups = new ConcurrentHashMap<NeighborSet, EcmpInfo>();
+        userDefinedGroups = new ConcurrentHashMap<Integer, EcmpInfo>();
         portNeighborSetMap =
                 new ConcurrentHashMap<PortNumber, ArrayList<NeighborSet>>();
         tunnelGroupIdTable = new ConcurrentHashMap<String, List<Integer>>();
@@ -227,8 +229,6 @@
     }
 
     public void removePortFromGroups(PortNumber port) {
-        /* FIX: removePortFromGroups is not working */
-
         log.debug("removePortFromGroups: Remove port {} from Switch {}",
                 port, getStringId());
         ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port);
@@ -891,7 +891,7 @@
                 Integer.parseInt(nodeId), bos);
         buckets.add(bucket);
         EcmpInfo ecmpInfo = new EcmpInfo(groupId, buckets);
-        setPolicyEcmpGroup(ecmpInfo);
+        setEcmpGroup(ecmpInfo);
 //        ecmpGroups.put(ns, ecmpInfo);
         log.debug(
                 "createGroupForANeighborSet: Creating ecmp group {} in sw {} "
@@ -1009,6 +1009,17 @@
             bos = b;
         }
 
+        BucketInfo(Dpid nDpid, MacAddress smac, MacAddress dmac,
+                PortNumber p, int label, boolean bos, int gotoGroupNo) {
+            neighborDpid = nDpid;
+            srcMac = smac;
+            dstMac = dmac;
+            outport = p;
+            mplsLabel = label;
+            this.bos = bos;
+            groupNo = gotoGroupNo;
+        }
+
 
         @Override
         public String toString() {
@@ -1025,95 +1036,30 @@
 
         List<OFBucket> buckets = new ArrayList<OFBucket>();
         for (BucketInfo b : ecmpInfo.buckets) {
-            OFOxmEthDst dmac = factory.oxms()
-                    .ethDst(b.dstMac);
-            OFAction setDA = factory.actions().buildSetField()
-                    .setField(dmac).build();
-            OFOxmEthSrc smac = factory.oxms()
-                    .ethSrc(b.srcMac);
-            OFAction setSA = factory.actions().buildSetField()
-                    .setField(smac).build();
-            OFAction outp = factory.actions().buildOutput()
-                    .setPort(OFPort.of(b.outport.shortValue()))
-                    .build();
             List<OFAction> actions = new ArrayList<OFAction>();
-            actions.add(setSA);
-            actions.add(setDA);
-            actions.add(outp);
-            if (b.mplsLabel != -1) {
-                OFAction pushLabel = factory.actions().buildPushMpls()
-                        .setEthertype(EthType.MPLS_UNICAST).build();
-                OFOxmMplsBos bosX = factory.oxms()
-                        .mplsBos(OFBooleanValue.TRUE);
-                OFAction setBX = factory.actions().buildSetField()
-                        .setField(bosX).build();
-                OFOxmMplsLabel lid = factory.oxms()
-                        .mplsLabel(U32.of(b.mplsLabel));
-                OFAction setLabel = factory.actions().buildSetField()
-                        .setField(lid).build();
-                OFAction copyTtl = factory.actions().copyTtlOut();
-                OFAction decrTtl = factory.actions().decMplsTtl();
-                actions.add(pushLabel);
-                actions.add(setLabel);
-                actions.add(setBX);
-                actions.add(copyTtl);
-                actions.add(decrTtl);
-            }
-            OFBucket ofb = factory.buildBucket()
-                    .setWeight(1)
-                    .setActions(actions)
-                    .build();
-            buckets.add(ofb);
-        }
-
-        OFMessage gm = factory.buildGroupAdd()
-                .setGroup(group)
-                .setBuckets(buckets)
-                .setGroupType(OFGroupType.SELECT)
-                .setXid(getNextTransactionId())
-                .build();
-        msglist.add(gm);
-        try {
-            write(msglist);
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-    }
-
-    private void setPolicyEcmpGroup(EcmpInfo ecmpInfo) {
-        List<OFMessage> msglist = new ArrayList<OFMessage>();
-        OFGroup group = OFGroup.of(ecmpInfo.groupId);
-
-        List<OFBucket> buckets = new ArrayList<OFBucket>();
-        List<OFAction> actions = new ArrayList<OFAction>();
-        for (BucketInfo b : ecmpInfo.buckets) {
-            if (b.dstMac != null && b.srcMac != null && b.outport != null) {
+            if (b.dstMac != null) {
                 OFOxmEthDst dmac = factory.oxms()
                         .ethDst(b.dstMac);
                 OFAction setDA = factory.actions().buildSetField()
                         .setField(dmac).build();
+                actions.add(setDA);
+            }
+            if (b.srcMac != null) {
                 OFOxmEthSrc smac = factory.oxms()
                         .ethSrc(b.srcMac);
                 OFAction setSA = factory.actions().buildSetField()
                         .setField(smac).build();
+                actions.add(setSA);
+            }
+            if (b.outport != null) {
                 OFAction outp = factory.actions().buildOutput()
                         .setPort(OFPort.of(b.outport.shortValue()))
                         .build();
-                actions.add(setSA);
-                actions.add(setDA);
                 actions.add(outp);
             }
-            if (b.groupNo > 0) {
-                OFAction groupTo = factory.actions().buildGroup()
-                        .setGroup(OFGroup.of(b.groupNo))
-                        .build();
-                actions.add(groupTo);
-            }
             if (b.mplsLabel != -1) {
                 OFAction pushLabel = factory.actions().buildPushMpls()
                         .setEthertype(EthType.MPLS_UNICAST).build();
-
                 OFBooleanValue bosValue = null;
                 if (b.bos)
                     bosValue = OFBooleanValue.TRUE;
@@ -1137,6 +1083,12 @@
                 if (b.bos)
                     actions.add(decrTtl);
             }
+            if (b.groupNo > 0) {
+                OFAction groupTo = factory.actions().buildGroup()
+                        .setGroup(OFGroup.of(b.groupNo))
+                        .build();
+                actions.add(groupTo);
+            }
             OFBucket ofb = factory.buildBucket()
                     .setWeight(1)
                     .setActions(actions)
@@ -1159,6 +1111,85 @@
         }
     }
 
+    /*
+        private void setPolicyEcmpGroup(EcmpInfo ecmpInfo) {
+            List<OFMessage> msglist = new ArrayList<OFMessage>();
+            OFGroup group = OFGroup.of(ecmpInfo.groupId);
+
+            List<OFBucket> buckets = new ArrayList<OFBucket>();
+            List<OFAction> actions = new ArrayList<OFAction>();
+            for (BucketInfo b : ecmpInfo.buckets) {
+                if (b.dstMac != null && b.srcMac != null && b.outport != null) {
+                    OFOxmEthDst dmac = factory.oxms()
+                            .ethDst(b.dstMac);
+                    OFAction setDA = factory.actions().buildSetField()
+                            .setField(dmac).build();
+                    OFOxmEthSrc smac = factory.oxms()
+                            .ethSrc(b.srcMac);
+                    OFAction setSA = factory.actions().buildSetField()
+                            .setField(smac).build();
+                    OFAction outp = factory.actions().buildOutput()
+                            .setPort(OFPort.of(b.outport.shortValue()))
+                            .build();
+                    actions.add(setSA);
+                    actions.add(setDA);
+                    actions.add(outp);
+                }
+                if (b.groupNo > 0) {
+                    OFAction groupTo = factory.actions().buildGroup()
+                            .setGroup(OFGroup.of(b.groupNo))
+                            .build();
+                    actions.add(groupTo);
+                }
+                if (b.mplsLabel != -1) {
+                    OFAction pushLabel = factory.actions().buildPushMpls()
+                            .setEthertype(EthType.MPLS_UNICAST).build();
+
+                    OFBooleanValue bosValue = null;
+                    if (b.bos)
+                        bosValue = OFBooleanValue.TRUE;
+                    else
+                        bosValue = OFBooleanValue.FALSE;
+                    OFOxmMplsBos bosX = factory.oxms()
+                            .mplsBos(bosValue);
+                    OFAction setBX = factory.actions().buildSetField()
+                            .setField(bosX).build();
+                    OFOxmMplsLabel lid = factory.oxms()
+                            .mplsLabel(U32.of(b.mplsLabel));
+                    OFAction setLabel = factory.actions().buildSetField()
+                            .setField(lid).build();
+                    OFAction copyTtl = factory.actions().copyTtlOut();
+                    OFAction decrTtl = factory.actions().decMplsTtl();
+                    actions.add(pushLabel);
+                    actions.add(setLabel);
+                    actions.add(setBX);
+                    actions.add(copyTtl);
+                    // decrement TTL only when the first MPLS label is pushed
+                    if (b.bos)
+                        actions.add(decrTtl);
+                }
+                OFBucket ofb = factory.buildBucket()
+                        .setWeight(1)
+                        .setActions(actions)
+                        .build();
+                buckets.add(ofb);
+            }
+
+            OFMessage gm = factory.buildGroupAdd()
+                    .setGroup(group)
+                    .setBuckets(buckets)
+                    .setGroupType(OFGroupType.SELECT)
+                    .setXid(getNextTransactionId())
+                    .build();
+            msglist.add(gm);
+            try {
+                write(msglist);
+            } catch (IOException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+    */
     private void deleteGroup(int groupId) {
 
         List<OFMessage> msglist = new ArrayList<OFMessage>();
@@ -1669,6 +1700,7 @@
     }
 
     @Override
+    @Deprecated
     public void createTunnel(String tunnelId, List<String> route, NeighborSet ns) {
 
         List<Integer> groups = new ArrayList<Integer>();
@@ -1718,6 +1750,141 @@
         return groups.get(0);
     }
 
+    private void createIndirectGroup(int groupId, MacAddress srcMac,
+            MacAddress dstMac, PortNumber outPort, int gotoGroupNo,
+            int mplsLabel, boolean bos) {
+        List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+        BucketInfo b = new BucketInfo(null, srcMac, dstMac, outPort,
+                mplsLabel, bos, gotoGroupNo);
+        buckets.add(b);
+
+        EcmpInfo ecmpInfo = new EcmpInfo(groupId, buckets);
+        setEcmpGroup(ecmpInfo);
+        log.debug(
+                "createIndirectGroup: Creating indirect group {} in sw {} "
+                        + "with: {}", groupId, getStringId(), ecmpInfo);
+        return;
+    }
+
+    private EcmpInfo createInnermostLabelGroup(int innermostGroupId,
+            List<PortNumber> ports, int mplsLabel, boolean bos,
+            HashMap<PortNumber, Integer> lastSetOfGroupIds) {
+        List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+        for (PortNumber sp : ports) {
+            Dpid neighborDpid = portToNeighbors.get(sp);
+            BucketInfo b = new BucketInfo(neighborDpid,
+                    MacAddress.of(srConfig.getRouterMac()),
+                    getNeighborRouterMacAddress(neighborDpid), null,
+                    mplsLabel, bos,
+                    lastSetOfGroupIds.get(sp));
+            buckets.add(b);
+        }
+        EcmpInfo ecmpInfo = new EcmpInfo(innermostGroupId, buckets);
+        setEcmpGroup(ecmpInfo);
+        log.debug(
+                "createInnermostLabelGroup: Creating indirect group {} in sw {} "
+                        + "with: {}", innermostGroupId, getStringId(), ecmpInfo);
+        return ecmpInfo;
+    }
+    @Override
+    /**
+     * Create a group chain with the same label stack for a given set of ports
+     * in the neighborset. This can be used for a basic scenario of tunnel based
+     * policy routing.
+     *
+     * @param labelStack list of router segment Ids to be pushed
+     * @param ns neighborSet to get to the first router in the labelStack
+     * NOTE:
+     *        The edgeLabel inside the neighborSet is ignored and user should
+     *        explicitly push that label on to the labelStack that is passed as
+     *        first argument
+     * @return group identifier
+     */
+    public int createGroup(List<Integer> labelStack, List<PortNumber> ports) {
+
+        if ((ports == null) ||
+                ((labelStack != null) && (labelStack.size() > 3))) {
+            log.warn("createGroup with wrong input parameters");
+        }
+        log.debug("createGroup with labelStack {} and ports {}",
+                labelStack, ports);
+
+        /* Create for each port, through which neighbors in ns are reachable,
+         * an indirect group with the outermost label
+         */
+        HashMap<PortNumber, Integer> lastSetOfGroupIds =
+                new HashMap<PortNumber, Integer>();
+        int innermostGroupId = -1;
+        if (labelStack.size() < 2) {
+            int curLabel = -1;
+            boolean bos = false;
+            if (labelStack.size()==1) {
+                curLabel = labelStack.get(0).intValue();
+                bos = true;
+            }
+
+            List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+            for (PortNumber sp : ports) {
+                Dpid neighborDpid = portToNeighbors.get(sp);
+                BucketInfo b = new BucketInfo(neighborDpid,
+                        MacAddress.of(srConfig.getRouterMac()),
+                        getNeighborRouterMacAddress(neighborDpid),
+                        sp, curLabel, bos, -1);
+                buckets.add(b);
+            }
+            innermostGroupId = groupid.incrementAndGet();
+            EcmpInfo ecmpInfo = new EcmpInfo(innermostGroupId, buckets);
+            setEcmpGroup(ecmpInfo);
+            userDefinedGroups.put(innermostGroupId, ecmpInfo);
+            return innermostGroupId;
+        }
+
+        for (int i = 0; i < labelStack.size(); i++) {
+            for (PortNumber sp : ports) {
+                if (i == 0) {
+                    /* Outermost label processing */
+                    int currGroupId = groupid.incrementAndGet();
+                    createIndirectGroup(currGroupId,
+                            null, null, sp, -1,
+                            labelStack.get(i).intValue(), false);
+                    lastSetOfGroupIds.put(sp, currGroupId);
+                }
+                else if (i == (labelStack.size() - 1)) {
+                    /* Innermost label processing */
+                    innermostGroupId = groupid.incrementAndGet();
+                    EcmpInfo topLevelGroup = createInnermostLabelGroup(
+                            innermostGroupId,
+                            ports,
+                            labelStack.get(i).intValue(), true,
+                            lastSetOfGroupIds);
+                    userDefinedGroups.put(
+                            innermostGroupId, topLevelGroup);
+                    break;
+                }
+                else {
+                    /* Middle label processing */
+                    int currGroupId = groupid.incrementAndGet();
+                    createIndirectGroup(currGroupId,
+                            null, null, null,
+                            lastSetOfGroupIds.get(sp),
+                            labelStack.get(i).intValue(), false);
+                    /* Overwrite with this iteration's group IDs */
+                    lastSetOfGroupIds.put(sp, currGroupId);
+                }
+            }
+        }
+        return innermostGroupId;
+    }
+
+    /**
+     * Remove the specified group
+     *
+     * @param groupId group identifier
+     * @return success/fail
+     */
+    public boolean removeGroup(int groupId) {
+        return false;
+    }
     @Override
     public Map<String, String> getPublishAttributes() {
         return publishAttributes;