Added the default routing feature for dell switch
 - If transit router && PHP is not involved, use output action isteadof group
 - SetSAAction and SetDAAction class are newly introduced for this
 - Added handler for the two new actions in driver

Change-Id: I8546087aa8676320a833d70784b27031f6092c30
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
index 4c9c1d5..2e89510 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -52,7 +52,9 @@
 import net.onrc.onos.core.matchaction.action.OutputAction;
 import net.onrc.onos.core.matchaction.action.PopMplsAction;
 import net.onrc.onos.core.matchaction.action.PushMplsAction;
+import net.onrc.onos.core.matchaction.action.SetDAAction;
 import net.onrc.onos.core.matchaction.action.SetMplsIdAction;
+import net.onrc.onos.core.matchaction.action.SetSAAction;
 import net.onrc.onos.core.matchaction.match.Ipv4Match;
 import net.onrc.onos.core.matchaction.match.Match;
 import net.onrc.onos.core.matchaction.match.MplsMatch;
@@ -82,6 +84,7 @@
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -114,8 +117,10 @@
     private HashMap<String, TunnelInfo> tunnelTable;
     private HashMap<Integer, HashMap<Integer, List<Integer>>> adjacencySidTable;
 
-    private int testMode = 0;
+    // Flag whether transit router supports ECMP or not
+    private boolean supportTransitECMP = true;
 
+    private int testMode = 0;
 
     private int numOfEvents = 0;
     private int numOfEventProcess = 0;
@@ -1023,88 +1028,14 @@
                 new ArrayList<MatchActionOperationEntry>();
         String fwdSw1 = fwdSws.get(0);
 
-        //If the next hop is the destination router
+        //If the next hop is the destination router, do PHP
         if (fwdSws.size() == 1 && mplsLabel.equals(getMplsLabel(fwdSw1))) {
-            // One rule for Bos = 1
-            MplsMatch mplsMatch = new MplsMatch(Integer.parseInt(mplsLabel), true);
-            List<Action> actions = new ArrayList<Action>();
-
-            PopMplsAction popAction = new PopMplsAction(EthType.IPv4);
-            CopyTtlInAction copyTtlInAction = new CopyTtlInAction();
-            DecNwTtlAction decNwTtlAction = new DecNwTtlAction(1);
-
-            actions.add(copyTtlInAction);
-            actions.add(popAction);
-            actions.add(decNwTtlAction);
-
-            GroupAction groupAction = new GroupAction();
-            groupAction.addSwitch(new Dpid(fwdSw1));
-            actions.add(groupAction);
-
-            MatchAction matchAction = new MatchAction(new MatchActionId(matchActionId++),
-                    new SwitchPort((long) 0, (short) 0), mplsMatch, actions);
-            Operator operator = Operator.ADD;
-            MatchActionOperationEntry maEntry =
-                    new MatchActionOperationEntry(operator, matchAction);
-            maEntries.add(maEntry);
-
-            // One rule for Bos = 0
-            MplsMatch mplsMatchBos = new MplsMatch(Integer.parseInt(mplsLabel), false);
-            List<Action> actionsBos = new ArrayList<Action>();
-            PopMplsAction popActionBos = new PopMplsAction(EthType.MPLS_UNICAST);
-            DecMplsTtlAction decMplsTtlAction = new DecMplsTtlAction(1);
-
-            actionsBos.add(copyTtlInAction);
-            actionsBos.add(popActionBos);
-            actionsBos.add(decMplsTtlAction);
-            actionsBos.add(groupAction);
-
-            MatchAction matchActionBos = new MatchAction(new MatchActionId(matchActionId++),
-                    new SwitchPort((long) 0, (short) 0), mplsMatchBos, actionsBos);
-            MatchActionOperationEntry maEntryBos =
-                    new MatchActionOperationEntry(operator, matchActionBos);
-            maEntries.add(maEntryBos);
+            maEntries.add(buildMAEntry(sw, mplsLabel, fwdSws, true, true));
+            maEntries.add(buildMAEntry(sw, mplsLabel, fwdSws, true, false));
         }
-        // If the next hop is NOT the destination router
         else {
-            // BoS = 0
-            MplsMatch mplsMatch = new MplsMatch(Integer.parseInt(mplsLabel), false);
-            List<Action> actions = new ArrayList<Action>();
-
-            DecMplsTtlAction decMplsTtlAction = new DecMplsTtlAction(1);
-            actions.add(decMplsTtlAction);
-
-            GroupAction groupAction = new GroupAction();
-            for (String fwdSw : fwdSws)
-                groupAction.addSwitch(new Dpid(fwdSw));
-            actions.add(groupAction);
-
-            MatchAction matchAction = new MatchAction(new MatchActionId(
-                    matchActionId++),
-                    new SwitchPort((long) 0, (short) 0), mplsMatch, actions);
-            Operator operator = Operator.ADD;
-            MatchActionOperationEntry maEntry =
-                    new MatchActionOperationEntry(operator, matchAction);
-            maEntries.add(maEntry);
-
-            // BoS = 1
-            MplsMatch mplsMatchBoS = new MplsMatch(Integer.parseInt(mplsLabel), true);
-            List<Action> actionsBoS = new ArrayList<Action>();
-
-            DecMplsTtlAction decMplsTtlActionBoS = new DecMplsTtlAction(1);
-            actionsBoS.add(decMplsTtlActionBoS);
-
-            GroupAction groupActionBoS = new GroupAction();
-            for (String fwdSw : fwdSws)
-                groupActionBoS.addSwitch(new Dpid(fwdSw));
-            actionsBoS.add(groupActionBoS);
-
-            MatchAction matchActionBos = new MatchAction(new MatchActionId(
-                    matchActionId++),
-                    new SwitchPort((long) 0, (short) 0), mplsMatchBoS, actionsBoS);
-            MatchActionOperationEntry maEntryBoS =
-                    new MatchActionOperationEntry(operator, matchActionBos);
-            maEntries.add(maEntryBoS);
+            maEntries.add(buildMAEntry(sw, mplsLabel, fwdSws, false, true));
+            maEntries.add(buildMAEntry(sw, mplsLabel, fwdSws, false, false));
         }
         IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
                 getSwId(sw.getDpid().toString()));
@@ -1120,6 +1051,7 @@
     }
 
 
+
     // ************************************
     // Policy routing classes and functions
     // ************************************
@@ -1675,13 +1607,14 @@
             }
             else {
                 if (!sw13.removeGroup(route.getGroupId())) {
-                    log.warn("Tunnel {} was not removed ", tunnelId);
+                    log.warn("Faied to remove the tunnel {} at driver",
+                            tunnelId);
                     return false;                }
             }
         }
 
         tunnelTable.remove(tunnelId);
-        log.debug("Tunnel {} was removed ", tunnelId);
+        log.debug("Tunnel {} was removed successfully.", tunnelId);
 
         return true;
     }
@@ -1783,10 +1716,19 @@
         return portList;
     }
 
-    private boolean isAdjacencySidNeighborOf(String prevNodeId, String prevAdjacencySid, String nodeId) {
+    /**
+     * Check whether the router with preNodeid is connected to the router
+     * with nodeId via adjacencySid or not
+     *
+     * @param prevNodeId the router node ID of the adjacencySid
+     * @param adjacencySid adjacency SID
+     * @param nodeId the router node ID to check
+     * @return
+     */
+    private boolean isAdjacencySidNeighborOf(String prevNodeId, String adjacencySid, String nodeId) {
 
         HashMap<Integer, List<Integer>> adjacencySidInfo = adjacencySidTable.get(Integer.valueOf(prevNodeId));
-        List<Integer> ports = adjacencySidInfo.get(Integer.valueOf(prevAdjacencySid));
+        List<Integer> ports = adjacencySidInfo.get(Integer.valueOf(adjacencySid));
 
         for (Integer port: ports) {
             Switch sw = getSwitchFromNodeId(prevNodeId);
@@ -1802,6 +1744,12 @@
         return false;
     }
 
+    /**
+     * Check if the node ID is the adjacency ID or not
+     *
+     * @param nodeId to check
+     * @return true if the node ID is the adjacency ID, false otherwise
+     */
     private boolean isAdjacencySid(String nodeId) {
         // XXX The rule might change
         if (Integer.parseInt(nodeId) > 10000)
@@ -1833,7 +1781,15 @@
         return  adjacencySidTable.get(Integer.valueOf(nodeSid));
     }
 
-    private HashMap<Integer, List<Integer>> parseAdjacencySidInfo(String adjInfo) throws JSONException {
+    /**
+     * Parse the adjacency jason string and build the adjacency Table
+     *
+     * @param adjInfo  adjacency info jason string
+     * @return HashMap<Adjacency ID, List of ports> object
+     * @throws JSONException
+     */
+    private HashMap<Integer, List<Integer>> parseAdjacencySidInfo(String adjInfo)
+            throws JSONException {
         JSONArray arry = new JSONArray(adjInfo);
         HashMap<Integer, List<Integer>> AdjacencyInfo =
                 new HashMap<Integer, List<Integer>>();
@@ -1854,6 +1810,112 @@
     }
 
     /**
+     * Build the MatchActionOperationEntry according to the flag
+     *
+     * @param sw node ID to push for MPLS label
+     * @param mplsLabel List of Switch DPIDs to forwards packets to
+     * @param fwdSws PHP flag
+     * @param Bos BoS flag
+     * @param isTransitRouter
+     * @return MatchiACtionOperationEntry object
+     */
+    private MatchActionOperationEntry buildMAEntry(Switch sw,
+            String mplsLabel, List<String> fwdSws, boolean php,
+            boolean Bos) {
+        MplsMatch mplsMatch = new MplsMatch(Integer.parseInt(mplsLabel), Bos);
+        List<Action> actions = new ArrayList<Action>();
+
+        PopMplsAction popActionBos = new PopMplsAction(EthType.IPv4);
+        PopMplsAction popAction = new PopMplsAction(EthType.MPLS_UNICAST);
+        CopyTtlInAction copyTtlInAction = new CopyTtlInAction();
+        DecNwTtlAction decNwTtlAction = new DecNwTtlAction(1);
+        DecMplsTtlAction decMplsTtlAction = new DecMplsTtlAction(1);
+
+        if (php) {
+            actions.add(copyTtlInAction);
+            if (Bos) {
+                actions.add(popActionBos);
+                actions.add(decNwTtlAction);
+            }
+            else {
+                actions.add(popAction);
+                actions.add(decMplsTtlAction);
+            }
+        }
+        else {
+            actions.add(decMplsTtlAction);
+        }
+
+        if (!supportTransitECMP && isTransitRouter(sw) && !php) {
+            PortNumber port = pickOnePort(sw, fwdSws);
+            if (port == null) {
+                log.warn("Failed to get a port from NeightborSet");
+                return null;
+            }
+            OutputAction outputAction = new OutputAction(port);
+            Switch destSwitch =
+                    mutableTopology.getSwitch(new Dpid(fwdSws.get(0)));
+            MacAddress srcMac =
+                    MacAddress.of(sw.getStringAttribute("routerMac"));
+            MacAddress dstMac =
+                    MacAddress.of(destSwitch.getStringAttribute("routerMac"));
+            SetSAAction setSAAction = new SetSAAction(srcMac);
+            SetDAAction setDAAction = new SetDAAction(dstMac);
+            actions.add(outputAction);
+            actions.add(setSAAction);
+            actions.add(setDAAction);
+        }
+        else {
+            GroupAction groupAction = new GroupAction();
+            for (String dpid: fwdSws)
+                groupAction.addSwitch(new Dpid(dpid));
+            actions.add(groupAction);
+        }
+
+        MatchAction matchAction = new MatchAction(new MatchActionId(matchActionId++),
+                new SwitchPort((long) 0, (short) 0), mplsMatch, actions);
+        Operator operator = Operator.ADD;
+        MatchActionOperationEntry maEntry =
+                new MatchActionOperationEntry(operator, matchAction);
+
+        return maEntry;
+    }
+
+    /**
+     * Pick a router from the neighbor set and return the port
+     * connected to the router.
+     *
+     * @param sw source switch
+     * @param fwdSwDpids neighbor set of the switch
+     * @return PortNumber connected to one of the neighbors
+     */
+    private PortNumber pickOnePort(Switch sw, List<String> fwdSwDpids) {
+        for (Link link: sw.getOutgoingLinks()) {
+            if (link.getDstSwitch().getDpid().toString().equals(fwdSwDpids.get(0)))
+                return link.getSrcPort().getNumber();
+        }
+
+        return null;
+    }
+
+    /**
+     * check if the router is the transit router or not
+     *
+     * @param sw  router switch to check
+     * @return true if the switch is the transit router, false otherwise
+     */
+    private boolean isTransitRouter(Switch sw) {
+        int i = 0;
+        for(Switch neighbor: sw.getNeighbors()) {
+            i++;
+        }
+        if (i > 1)
+            return true;
+        else
+            return false;
+    }
+
+    /**
      * Get the forwarding Switch DPIDs to send packets to a node
      *
      * @param srcSw source switch
@@ -2125,7 +2187,6 @@
         arpHandler.sendArpRequest(sw, destinationAddress, inPort);
     }
 
-
     // ************************************
     // Test functions
     // ************************************
@@ -2318,7 +2379,6 @@
 
     }
 
-
     // ************************************
     // Unused classes and functions
     // ************************************
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 f426428..874232f 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -48,8 +48,10 @@
 import net.onrc.onos.core.matchaction.action.OutputAction;
 import net.onrc.onos.core.matchaction.action.PopMplsAction;
 import net.onrc.onos.core.matchaction.action.PushMplsAction;
+import net.onrc.onos.core.matchaction.action.SetDAAction;
 import net.onrc.onos.core.matchaction.action.SetMplsBosAction;
 import net.onrc.onos.core.matchaction.action.SetMplsIdAction;
+import net.onrc.onos.core.matchaction.action.SetSAAction;
 import net.onrc.onos.core.matchaction.match.Ipv4Match;
 import net.onrc.onos.core.matchaction.match.Match;
 import net.onrc.onos.core.matchaction.match.MplsMatch;
@@ -1361,6 +1363,16 @@
             ofAction = factory.actions().copyTtlIn();
         } else if (action instanceof CopyTtlOutAction) {
             ofAction = factory.actions().copyTtlOut();
+        } else if (action instanceof SetDAAction) {
+            OFOxmEthDst dmac = factory.oxms()
+                    .ethDst(((SetDAAction)action).getAddress());
+            ofAction = factory.actions().buildSetField()
+                    .setField(dmac).build();
+        } else if (action instanceof SetSAAction) {
+            OFOxmEthSrc smac = factory.oxms()
+                    .ethSrc(((SetSAAction)action).getAddress());
+            ofAction = factory.actions().buildSetField()
+                    .setField(smac).build();
         } else {
             log.warn("Unsupported Action type: {}", action.getClass().getName());
             return null;
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java
new file mode 100644
index 0000000..6a28e7b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/SetDAAction.java
@@ -0,0 +1,15 @@
+package net.onrc.onos.core.matchaction.action;
+
+import org.projectfloodlight.openflow.types.MacAddress;
+
+public class SetDAAction implements Action {
+    MacAddress macAddress;
+
+    public SetDAAction(MacAddress macAddress) {
+        this.macAddress = macAddress;
+    }
+
+    public MacAddress getAddress() {
+        return this.macAddress;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/SetSAAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/SetSAAction.java
new file mode 100644
index 0000000..0281746
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/SetSAAction.java
@@ -0,0 +1,17 @@
+package net.onrc.onos.core.matchaction.action;
+
+import org.projectfloodlight.openflow.types.MacAddress;
+
+public class SetSAAction implements Action {
+
+    MacAddress macAddress;
+
+    public SetSAAction (MacAddress macAddress) {
+        this.macAddress = macAddress;
+    }
+
+    public MacAddress getAddress() {
+        return this.macAddress;
+    }
+
+}