- Modified Semgment Routing according to the change of driver
 -- Now the appilcation use greateGroup() of driver when creating tunnels
- Check Adjacency ID when creating tunnel

Change-Id: I9d8898f8b8cfda9612b14c5095c1598a1cb4bc3c
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 5b3f338..e300ee2 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -112,7 +112,7 @@
     private ConcurrentLinkedQueue<TopologyEvents> topologyEventQueue;
     private HashMap<String, PolicyInfo> policyTable;
     private HashMap<String, TunnelInfo> tunnelTable;
-    private HashMap<Integer, HashMap<Integer, List<Integer>>> adjacencyIdTable;
+    private HashMap<Integer, HashMap<Integer, List<Integer>>> adjacencySidTable;
 
     private int testMode = 0;
 
@@ -186,7 +186,7 @@
         restApi = context.getServiceImpl(IRestApiService.class);
         policyTable = new HashMap<String, PolicyInfo>();
         tunnelTable = new HashMap<String, TunnelInfo>();
-        adjacencyIdTable = new HashMap<Integer,HashMap<Integer, List<Integer>>>();
+        adjacencySidTable = new HashMap<Integer,HashMap<Integer, List<Integer>>>();
 
         packetService.registerPacketListener(this);
         topologyService.addListener(this, false);
@@ -597,43 +597,87 @@
             return;
 
         // parse adjacency Id
-        List<HashMap<Integer, List<Integer>>> adjacncyInfoList =
-                new ArrayList<HashMap<Integer, List<Integer>>>();
         HashMap<Integer, List<Integer>> adjacencyInfo = null;
         if (adjInfo != null) {
             adjacencyInfo = parseAdjacencySidInfo(adjInfo);
-            //adjacncyInfoList.add(adjacencyInfo);
         }
         // parse auto generated adjacency Id
         adjacencyInfo.putAll(parseAdjacencySidInfo(autoAdjInfo));
 
-        adjacencyIdTable.put(Integer.parseInt(nodeSidStr), adjacencyInfo);
+        adjacencySidTable.put(Integer.parseInt(nodeSidStr), adjacencyInfo);
 
-            /*
-            Dpid dstDpid = null;
-            for (Link link: sw.getOutgoingLinks()) {
-                if (link.getSrcPort().getPortNumber().value() == portNos[1]) {
-                    dstDpid = link.getDstPort().getDpid();
-                    break;
-                }
-            }
-            if (dstDpid == null) {
-                log.debug("Cannot find the destination switch for the adjacency ID {}", adjId);
-                continue;
-            }
-            Switch dstSw = mutableTopology.getSwitch(dstDpid);
-            String dstMac = null;
-            if (dstSw == null) {
-                log.debug("Cannot find SW {}", dstDpid.toString());
-                continue;
+        for (Integer adjId: adjacencyInfo.keySet()) {
+            List<Integer> ports = adjacencyInfo.get(adjId);
+            if (ports.size() == 1) {
+                setAdjacencyRuleOfOutput(sw, adjId, srcMac, ports.get(0));
             }
             else {
-                dstMac = dstSw.getStringAttribute("routerMac");
+                setAdjacencyRuleOfGroup(sw, adjId, ports);
             }
+        }
+    }
 
-            setAdjRule(sw, adjId, srcMac, dstMac, portNo, true); // BoS = 1
-            setAdjRule(sw, adjId, srcMac, dstMac, portNo, false); // BoS = 0
-            */
+    /**
+     * Set Adjacency Rule to MPLS table for adjacency Ids attached to multiple
+     * ports
+     *
+     * @param sw Switch
+     * @param adjId Adjacency ID
+     * @param ports List of ports assigned to the Adjacency ID
+     */
+    private void setAdjacencyRuleOfGroup(Switch sw, Integer adjId, List<Integer> ports) {
+
+        IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
+                getSwId(sw.getDpid().toString()));
+
+        int groupId = -1;
+        if (sw13 != null) {
+            List<PortNumber> portList = new ArrayList<PortNumber>();
+            for (Integer port: ports)
+                portList.add(PortNumber.uint32(port));
+            groupId = sw13.createGroup(new ArrayList<Integer>(), portList);
+        }
+
+        if (groupId < 0) {
+            log.debug("Failed to create a group at driver for adj ID {}", adjId);
+        }
+
+        pushAdjRule(sw, adjId, null, null, groupId, true);
+        pushAdjRule(sw, adjId, null, null, groupId, false);
+    }
+
+    /**
+     * Set Adjacency Rule to MPLS table for adjacency Ids attached to single port
+     *
+     * @param sw Switch
+     * @param adjId Adjacency ID
+     * @param ports List of ports assigned to the Adjacency ID
+     */
+    private void setAdjacencyRuleOfOutput(Switch sw, Integer adjId, String srcMac, Integer portNo) {
+
+        Dpid dstDpid = null;
+        for (Link link: sw.getOutgoingLinks()) {
+            if (link.getSrcPort().getPortNumber().value() == portNo) {
+                dstDpid = link.getDstPort().getDpid();
+                break;
+            }
+        }
+        if (dstDpid == null) {
+            log.debug("Cannot find the destination switch for the adjacency ID {}", adjId);
+            return;
+        }
+        Switch dstSw = mutableTopology.getSwitch(dstDpid);
+        String dstMac = null;
+        if (dstSw == null) {
+            log.debug("Cannot find SW {}", dstDpid.toString());
+            return;
+        }
+        else {
+            dstMac = dstSw.getStringAttribute("routerMac");
+        }
+
+        pushAdjRule(sw, adjId, srcMac, dstMac, portNo, true); // BoS = 1
+        pushAdjRule(sw, adjId, srcMac, dstMac, portNo, false); // BoS = 0
 
     }
 
@@ -647,8 +691,8 @@
      * @param portNo  port number assigned to the ID
      * @param bos  BoS option
      */
-    private void setAdjRule(Switch sw, int id, String srcMac, String dstMac, int portNo,
-            boolean bos) {
+    private void pushAdjRule(Switch sw, int id, String srcMac, String dstMac,
+            int num, boolean bos) {
 
         MplsMatch mplsMatch = new MplsMatch(id, bos);
         List<Action> actions = new ArrayList<Action>();
@@ -668,13 +712,22 @@
             actions.add(decMplsTtlAction);
         }
 
-        ModifyDstMacAction setDstAction = new ModifyDstMacAction(MACAddress.valueOf(srcMac));
-        ModifySrcMacAction setSrcAction = new ModifySrcMacAction(MACAddress.valueOf(dstMac));
-        OutputAction outportAction = new OutputAction(PortNumber.uint32(portNo));
+        // Output action
+        if (srcMac != null && dstMac != null) {
+            ModifyDstMacAction setDstAction = new ModifyDstMacAction(MACAddress.valueOf(srcMac));
+            ModifySrcMacAction setSrcAction = new ModifySrcMacAction(MACAddress.valueOf(dstMac));
+            OutputAction outportAction = new OutputAction(PortNumber.uint32(num));
 
-        actions.add(setDstAction);
-        actions.add(setSrcAction);
-        actions.add(outportAction);
+            actions.add(setDstAction);
+            actions.add(setSrcAction);
+            actions.add(outportAction);
+        }
+        // Group Action
+        else {
+            GroupAction groupAction = new GroupAction();
+            groupAction.setGroupId(num);
+            actions.add(groupAction);
+        }
 
         MatchAction matchAction = new MatchAction(new MatchActionId(matchActionId++),
                 new SwitchPort((long) 0, (short) 0), mplsMatch, actions);
@@ -1121,7 +1174,8 @@
         private List<Integer> labelIds;
         private List<TunnelRouteInfo> routes;
 
-        public TunnelInfo(String tid, List<Integer> labelIds, List<TunnelRouteInfo> routes) {
+        public TunnelInfo(String tid, List<Integer> labelIds,
+                List<TunnelRouteInfo> routes) {
             this.tunnelId = tid;
             this.labelIds = labelIds;
             this.routes = routes;
@@ -1143,6 +1197,7 @@
         private String srcSwDpid;
         private List<Dpid> fwdSwDpids;
         private List<String> route;
+        private int gropuId;
 
         public TunnelRouteInfo() {
             fwdSwDpids = new ArrayList<Dpid>();
@@ -1165,6 +1220,10 @@
             this.route = r;
         }
 
+        private void setGroupId(int groupId) {
+            this.gropuId = groupId;
+        }
+
         public String getSrcSwDpid() {
             return this.srcSwDpid;
         }
@@ -1176,6 +1235,10 @@
         public List<String> getRoute() {
             return this.route;
         }
+
+        public int getGroupId() {
+            return this.gropuId;
+        }
     }
 
     /**
@@ -1213,15 +1276,14 @@
      * @return the first group ID of the tunnel
      */
     public int getTunnelGroupId(String tunnelId, String dpid) {
-        IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
-                getSwId(dpid));
+       TunnelInfo tunnelInfo = tunnelTable.get(tunnelId);
+       for (TunnelRouteInfo routeInfo: tunnelInfo.getRoutes()) {
+           String tunnelSrcDpid = routeInfo.getSrcSwDpid();
+           if (tunnelSrcDpid.equals(dpid))
+               return routeInfo.getGroupId();
+        }
 
-        if (sw13 == null) {
-            return -1;
-        }
-        else {
-            return sw13.getTunnelGroupId(tunnelId);
-        }
+        return -1;
     }
 
     /**
@@ -1246,33 +1308,51 @@
 
         List<TunnelRouteInfo> stitchingRule = getStitchingRule(Ids);
         if (stitchingRule == null) {
-            log.debug("Failed to get the policy rule.");
+            log.debug("Failed to get a tunnel rule.");
             return false;
         }
         for (TunnelRouteInfo route: stitchingRule) {
-
-            IOF13Switch targetSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
-                    getSwId(route.srcSwDpid));
-
-            if (targetSw == null) {
-                log.debug("Switch {} is gone.", route.srcSwDpid);
-                return false;
-            }
-
             NeighborSet ns = new NeighborSet();
             for (Dpid dpid: route.getFwdSwDpid())
                 ns.addDpid(dpid);
 
-            printTunnelInfo(targetSw, tunnelId, route.getRoute(), ns);
-            targetSw.createTunnel(tunnelId, route.getRoute(), ns);
+            printTunnelInfo(route.srcSwDpid, tunnelId, route.getRoute(), ns);
+            int groupId = -1;
+            if ((groupId =createGroupsForTunnel(tunnelId, route, ns)) < 0) {
+                log.debug("Failed to create a tunnel at driver.");
+                return false;
+            }
+            route.setGroupId(groupId);
         }
 
-        TunnelInfo tunnelInfo = new TunnelInfo(tunnelId, labelIds, stitchingRule);
+        TunnelInfo tunnelInfo = new TunnelInfo(tunnelId, labelIds,
+                stitchingRule);
         tunnelTable.put(tunnelId, tunnelInfo);
 
         return true;
     }
 
+    private int createGroupsForTunnel(String tunnelId, TunnelRouteInfo routeInfo,
+            NeighborSet ns) {
+
+        IOF13Switch targetSw = (IOF13Switch) floodlightProvider.getMasterSwitch(
+                getSwId(routeInfo.srcSwDpid));
+
+        if (targetSw == null) {
+            log.debug("Switch {} is gone.", routeInfo.srcSwDpid);
+            return -1;
+        }
+
+        List<Integer> Ids = new ArrayList<Integer>();
+        for (String IdStr: routeInfo.route)
+            Ids.add(Integer.parseInt(IdStr));
+
+        List<PortNumber> ports = getPortsFromNeighborSet(routeInfo.srcSwDpid, ns);
+        int groupId = targetSw.createGroup(Ids, ports);
+
+        return groupId;
+    }
+
     /**
      * Set policy table for policy routing
      *
@@ -1306,12 +1386,16 @@
             packetBuilder.setDstTcpPort(dstTcpPort);
         PacketMatch policyMatch = packetBuilder.build();
         TunnelInfo tunnelInfo = tunnelTable.get(tid);
+        if (tunnelInfo == null) {
+            log.debug("Tunnel {} is not defined", tid);
+            return false;
+        }
         List<TunnelRouteInfo> routes = tunnelInfo.routes;
 
         for (TunnelRouteInfo route : routes) {
             List<Action> actions = new ArrayList<>();
             GroupAction groupAction = new GroupAction();
-            groupAction.setTunnelId(tid);
+            groupAction.setGroupId(route.getGroupId());
             actions.add(groupAction);
 
             MatchAction matchAction = new MatchAction(new MatchActionId(
@@ -1357,8 +1441,13 @@
         List<TunnelRouteInfo> rules = new ArrayList<TunnelRouteInfo>();
 
         Switch srcSw = this.getSwitchFromNodeId(route.get(0));
+        if (srcSw == null) {
+            log.warn("Switch is not found for Node SID {}", route.get(0));
+            return null;
+        }
         String srcDpid = srcSw.getDpid().toString();
 
+        /*
         if (route.size() <= MAX_NUM_LABELS+1) {
             boolean match =false;
             TunnelRouteInfo routeInfo = new TunnelRouteInfo();
@@ -1388,47 +1477,73 @@
             rules.add(routeInfo);
             return rules;
         }
+        */
 
         int i = 0;
         TunnelRouteInfo routeInfo = new TunnelRouteInfo();
         boolean checkNeighbor = true;
+        String prevAdjacencySid = null;
+        String prevNodeId = null;
 
         for (String nodeId: route) {
-            // First node ID is always the source router
+            // The first node ID is always the source router.
+            // We assume that the first ID cannot be an Adjacency SID.
             if (i == 0) {
                 routeInfo.setSrcDpid(srcDpid);
                 srcSw = getSwitchFromNodeId(nodeId);
                 i++;
             }
-            else if (i == 1 && checkNeighbor) {
-                // Check if next node is the neighbor SW of the source SW
-                List<Dpid> fwdSwDpids = getForwardingSwitchForNodeId(srcSw,
-                        nodeId);
-                if (fwdSwDpids == null || fwdSwDpids.isEmpty()) {
-                    log.debug("There is no route from node {} to node {}",
-                            srcSw.getDpid(), nodeId);
-                    return null;
-                }
-                // If first Id is one of the neighbors, do not include it to route, but set it as a fwd SW.
-                boolean match = false;
-                for (Dpid dpid: fwdSwDpids) {
-                    if (getMplsLabel(dpid.toString()).toString().equals(nodeId)) {
-                        List<Dpid> fwdSws = new ArrayList<Dpid>();
-                        fwdSws.add(dpid);
-                        routeInfo.setFwdSwDpid(fwdSws);
-                        match = true;
-                        break;
-                    }
-                }
-                if (!match) {
+            else if (i == 1) {
+                if (isAdjacencySid(nodeId)) {
                     routeInfo.addRoute(nodeId);
-                    routeInfo.setFwdSwDpid(fwdSwDpids);
+                    i++;
+                    prevAdjacencySid = nodeId;
+                }
+                else if (checkNeighbor) {
+                    // Check if next node is the neighbor SW of the source SW
+                    List<Dpid> fwdSwDpids = getForwardingSwitchForNodeId(srcSw,
+                            nodeId);
+                    if (fwdSwDpids == null || fwdSwDpids.isEmpty()) {
+                        log.debug("There is no route from node {} to node {}",
+                                srcSw.getDpid(), nodeId);
+                        return null;
+                    }
+                    // If first Id is one of the neighbors, do not include it to route, but set it as a fwd SW.
+                    boolean match = false;
+                    for (Dpid dpid: fwdSwDpids) {
+                        if (getMplsLabel(dpid.toString()).toString().equals(nodeId)) {
+                            List<Dpid> fwdSws = new ArrayList<Dpid>();
+                            fwdSws.add(dpid);
+                            routeInfo.setFwdSwDpid(fwdSws);
+                            match = true;
+                            break;
+                        }
+                    }
+                    if (!match) {
+                        routeInfo.addRoute(nodeId);
+                        routeInfo.setFwdSwDpid(fwdSwDpids);
+                        i++;
+                    }
+                    // we check only the next node ID of the source router
+                    checkNeighbor = false;
+
+                // if we don't need to check neighbor
+                }else {
+                    routeInfo.addRoute(nodeId);
                     i++;
                 }
-                // we check only the next node ID of the source router
-                checkNeighbor = false;
             }
+            // if i > 1
             else {
+                // If the adjacency SID is pushed and the next SID is the destination
+                // of the adjacency SID, then do not add the SID.
+                if (prevAdjacencySid != null) {
+                    if (isAdjacencySidNeighborOf(prevNodeId, prevAdjacencySid, nodeId)) {
+                        prevAdjacencySid = null;
+                        continue;
+                    }
+                    prevAdjacencySid = null;
+                }
                 routeInfo.addRoute(nodeId);
                 i++;
             }
@@ -1443,6 +1558,9 @@
                 i = 1;
                 checkNeighbor = true;
             }
+
+            if (prevAdjacencySid == null)
+                prevNodeId = nodeId;
         }
 
         if (i < MAX_NUM_LABELS+1) {
@@ -1561,6 +1679,47 @@
     // Utility functions
     // ************************************
 
+    private List<PortNumber> getPortsFromNeighborSet(String srcSwDpid, NeighborSet ns) {
+
+        List<PortNumber> portList = new ArrayList<PortNumber>();
+        Switch srcSwitch = mutableTopology.getSwitch(new Dpid(srcSwDpid));
+        if (srcSwitch == null)
+            return null;
+        for (Dpid neighborDpid: ns.getDpids()) {
+            Link link = srcSwitch.getLinkToNeighbor(neighborDpid);
+            portList.add(link.getSrcPort().getNumber());
+        }
+
+        return portList;
+    }
+
+    private boolean isAdjacencySidNeighborOf(String prevNodeId, String prevAdjacencySid, String nodeId) {
+
+        HashMap<Integer, List<Integer>> adjacencySidInfo = adjacencySidTable.get(Integer.valueOf(prevNodeId));
+        List<Integer> ports = adjacencySidInfo.get(Integer.valueOf(prevAdjacencySid));
+
+        for (Integer port: ports) {
+            Switch sw = getSwitchFromNodeId(prevNodeId);
+            for (Link link: sw.getOutgoingLinks()) {
+                if (link.getSrcPort().getPortNumber().value() == port) {
+                    if (getMplsLabel(link.getDstPort().getDpid().toString()).equals(nodeId)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isAdjacencySid(String nodeId) {
+        // XXX The rule might change
+        if (Integer.parseInt(nodeId) > 10000)
+            return true;
+
+        return false;
+    }
+
     /**
      * Returns the Adjacency IDs for the node
      *
@@ -1569,7 +1728,7 @@
      */
     public Collection<Integer> getAdjacencyIds(int nodeSid) {
         HashMap<Integer, List<Integer>> adjecencyInfo =
-                adjacencyIdTable.get(Integer.valueOf(nodeSid));
+                adjacencySidTable.get(Integer.valueOf(nodeSid));
 
         return adjecencyInfo.keySet();
     }
@@ -1581,7 +1740,7 @@
      * @return HashMap of <AdjacencyID, list of ports>
      */
     public HashMap<Integer, List<Integer>> getAdjacencyInfo(int nodeSid) {
-        return  adjacencyIdTable.get(Integer.valueOf(nodeSid));
+        return  adjacencySidTable.get(Integer.valueOf(nodeSid));
     }
 
     private HashMap<Integer, List<Integer>> parseAdjacencySidInfo(String adjInfo) throws JSONException {
@@ -1987,10 +2146,10 @@
      * @param ids
      * @param tunnelId
      */
-    private void printTunnelInfo(IOF13Switch targetSw, String tunnelId,
+    private void printTunnelInfo(String targetSw, String tunnelId,
             List<String> ids, NeighborSet ns) {
         StringBuilder logStr = new StringBuilder("In switch " +
-            targetSw.getId() + ", create a tunnel " + tunnelId + " " + " of push ");
+                targetSw + ", create a tunnel " + tunnelId + " " + " of push ");
         for (String id: ids)
             logStr.append(id + "-");
         logStr.append(" output to ");