Added new class files

Change-Id: Iaf24992560e59e7552e39fd4610d5c669d0afaa7
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingTunnel.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingTunnel.java
new file mode 100644
index 0000000..9eea762
--- /dev/null
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingTunnel.java
@@ -0,0 +1,510 @@
+package net.onrc.onos.apps.segmentrouting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.floodlightcontroller.core.IOF13Switch;
+import net.floodlightcontroller.core.IOF13Switch.NeighborSet;
+import net.onrc.onos.core.drivermanager.OFSwitchImplDellOSR;
+import net.onrc.onos.core.topology.Link;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.PortNumber;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SegmentRoutingTunnel {
+
+    private static final Logger log = LoggerFactory
+            .getLogger(SegmentRoutingTunnel.class);
+
+    private String tunnelId;
+    private List<Integer> labelIds;
+    private List<TunnelRouteInfo> routes;
+    private SegmentRoutingManager srManager;
+
+    private final int MAX_NUM_LABELS = 3;
+
+    public SegmentRoutingTunnel(SegmentRoutingManager srm, String tid,
+            List<Integer> labelIds) {
+        this.srManager = srm;
+        this.tunnelId = tid;
+        this.labelIds = labelIds;
+        this.routes = new ArrayList<TunnelRouteInfo>();
+    }
+
+    public String getTunnelId(){
+        return this.tunnelId;
+    }
+
+    public List<Integer> getLabelids() {
+        return this.labelIds;
+    }
+
+    public List<TunnelRouteInfo> getRoutes(){
+        return this.routes;
+    }
+
+    public boolean createTunnel() {
+
+        if (labelIds.isEmpty() || labelIds.size() < 2) {
+            log.debug("Wrong tunnel information");
+            return false;
+        }
+
+        List<String> Ids = new ArrayList<String>();
+        for (Integer label : labelIds) {
+            Ids.add(label.toString());
+        }
+
+        List<TunnelRouteInfo> stitchingRule = getStitchingRule(Ids);
+        if (stitchingRule == null) {
+            log.debug("Failed to get a tunnel rule.");
+            return false;
+        }
+
+        for (TunnelRouteInfo route: stitchingRule) {
+            NeighborSet ns = new NeighborSet();
+            for (Dpid dpid: route.getFwdSwDpid())
+                ns.addDpid(dpid);
+
+            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);
+        }
+
+        this.routes = stitchingRule;
+
+        return true;
+    }
+
+    public boolean removeTunnel() {
+
+        for (TunnelRouteInfo route: routes) {
+            IOF13Switch sw13 = srManager.getIOF13Switch(route.srcSwDpid);
+            if (sw13 == null) {
+                log.warn("Cannot find the switch", route.srcSwDpid);
+                return false;
+            }
+            else {
+                if (!sw13.removeGroup(route.getGroupId())) {
+                    log.warn("Faied to remove the tunnel {} at driver",
+                            tunnelId);
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Create groups for the tunnel
+     *
+     * @param tunnelId tunnel ID
+     * @param routeInfo label stacks for the tunnel
+     * @param ns NeighborSet to forward packets
+     * @return group ID, return -1 if it fails
+     */
+    private int createGroupsForTunnel(String tunnelId, TunnelRouteInfo routeInfo,
+            NeighborSet ns) {
+
+        IOF13Switch targetSw = srManager.getIOF13Switch(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;
+    }
+
+
+    /**
+     * Split the nodes IDs into multiple tunnel if Segment Stitching is required.
+     * We assume that the first node ID is the one of source router, and the last
+     * node ID is that of the destination router.
+     *
+     * @param route list of node IDs
+     * @return List of the TunnelRoutInfo
+     */
+    private List<TunnelRouteInfo> getStitchingRule(List<String> route) {
+
+        if (route.isEmpty() || route.size() < 3)
+            return null;
+
+        List<TunnelRouteInfo> rules = new ArrayList<TunnelRouteInfo>();
+
+        Switch srcSw = srManager.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();
+
+        int i = 0;
+        TunnelRouteInfo routeInfo = new TunnelRouteInfo();
+        boolean checkNeighbor = false;
+        String prevAdjacencySid = null;
+        String prevNodeId = null;
+
+        for (String nodeId: route) {
+            // The first node ID is always the source router.
+            // We assume that the first ID cannot be an Adjacency SID.
+            if (i == 0) {
+                srcSw = srManager.getSwitchFromNodeId(nodeId);
+                if (srcDpid == null)
+                    srcDpid = srcSw.getDpid().toString();
+                routeInfo.setSrcDpid(srcDpid);
+                checkNeighbor = true;
+                i++;
+            }
+            // if this is the first node ID to put the label stack..
+            else if (i == 1) {
+                if (checkNeighbor) {
+                    List<Dpid> fwdSws = getDpidIfNeighborOf(nodeId, srcSw);
+                    // if nodeId is NOT the neighbor of srcSw..
+                    if (fwdSws.isEmpty()) {
+                        fwdSws = srManager.getForwardingSwitchForNodeId(srcSw,nodeId);
+                        if (fwdSws == null || fwdSws.isEmpty()) {
+                            log.warn("There is no route from node {} to node {}",
+                                    srcSw.getDpid(), nodeId);
+                            return null;
+                        }
+                        routeInfo.addRoute(nodeId);
+                        i++;
+                    }
+                    routeInfo.setFwdSwDpid(fwdSws);
+                    // we check only the next node ID of the source router
+                    checkNeighbor = false;
+                }
+                // if neighbor check is already done, then just add it
+                else  {
+                    routeInfo.addRoute(nodeId);
+                    i++;
+                }
+            }
+            // 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;
+                        prevNodeId = nodeId;
+                        continue;
+                    }
+                    prevAdjacencySid = null;
+                }
+                routeInfo.addRoute(nodeId);
+                i++;
+            }
+
+            // If the adjacency ID is added the label stack,
+            // then we need to check if the next node is the destination of the adjacency SID
+            if (srManager.isAdjacencySid(nodeId))
+                prevAdjacencySid = nodeId;
+
+            // If the number of labels reaches the limit, start over the procedure
+            if (i == MAX_NUM_LABELS +1) {
+
+                rules.add(routeInfo);
+                routeInfo = new TunnelRouteInfo();
+
+                if (srManager.isAdjacencySid(nodeId)) {
+                    // If the previous sub tunnel finishes with adjacency SID,
+                    // then we need to start the procedure from the adjacency
+                    // destination ID.
+                    List<Switch> destNodeList =
+                            getAdjacencyDestinationNode(prevNodeId, nodeId);
+                    if (destNodeList == null || destNodeList.isEmpty()) {
+                        log.warn("Cannot find destination node for adjacencySID {}",
+                                nodeId);
+                        return null;
+                    }
+                    // If the previous sub tunnel finishes with adjacency SID with
+                    // multiple ports, then we need to remove the adjacency Sid
+                    // from the previous sub tunnel and start the new sub tunnel
+                    // with the adjacency Sid. Technically, the new subtunnel
+                    // forward packets to the port assigned to the adjacency Sid
+                    // and the label stack starts with the next ID.
+                    // This is to avoid to install new policy rule to multiple nodes for stitching when the
+                    // adjacency Sid that has more than one port.
+                    if (destNodeList.size() > 1) {
+                        rules.get(rules.size()-1).route.remove(nodeId);
+                        srcSw = srManager.getSwitchFromNodeId(prevNodeId);
+                        List<Dpid> fwdSws = getDpidIfNeighborOf(nodeId, srcSw);
+                        routeInfo.setFwdSwDpid(fwdSws);
+                        routeInfo.setSrcDpid(srcSw.getDpid().toString());
+                        i = 1;
+                        checkNeighbor = false;
+                        continue;
+                    }
+                    else {
+                        srcSw = destNodeList.get(0);
+                    }
+                }
+                else {
+                    srcSw = srManager.getSwitchFromNodeId(nodeId);
+                }
+                srcDpid = srcSw.getDpid().toString();
+                routeInfo.setSrcDpid(srcDpid);
+                i = 1;
+                checkNeighbor = true;
+            }
+
+            if (prevAdjacencySid == null)
+                prevNodeId = nodeId;
+        }
+
+
+        if (i < MAX_NUM_LABELS+1 && (routeInfo.getFwdSwDpid() != null &&
+                !routeInfo.getFwdSwDpid().isEmpty())) {
+            rules.add(routeInfo);
+            // NOTE: empty label stack can happen, but forwarding destination should be set
+        }
+
+        return rules;
+    }
+
+
+    /**
+     * Get port numbers of the neighbor set.
+     * If ECMP in transit router is not supported, then only one port should be returned
+     * regardless of number of nodes in neighbor set.
+     *
+     * @param srcSwDpid source switch
+     * @param ns Neighbor set of the switch
+     * @return List of PortNumber, null if not found
+     */
+    private List<PortNumber> getPortsFromNeighborSet(String srcSwDpid, NeighborSet ns) {
+
+        List<PortNumber> portList = new ArrayList<PortNumber>();
+        Switch srcSwitch = srManager.getSwitch(srcSwDpid);
+        if (srcSwitch == null)
+            return null;
+        IOF13Switch srcSwitch13 =
+                srManager.getIOF13Switch(srcSwitch.getDpid().toString());
+
+        for (Dpid neighborDpid: ns.getDpids()) {
+            if (srcSwitch13 instanceof OFSwitchImplDellOSR &&
+                    ns.getDpids().size() == 1) {
+                Switch dstSwitch = srManager.getSwitch(neighborDpid.toString());
+                if (srManager.isTransitRouter(srcSwitch) &&
+                        srManager.isTransitRouter(dstSwitch)) {
+                    Link link = srcSwitch.getLinkToNeighbor(neighborDpid);
+                    portList.add(link.getSrcPort().getNumber());
+                    break;
+                }
+            }
+            else {
+                for (Link link: srcSwitch.getOutgoingLinks()) {
+                    if (link.getDstSwitch().getDpid().equals(neighborDpid)) {
+                        portList.add(link.getSrcPort().getNumber());
+                    }
+                }
+            }
+        }
+
+        return portList;
+    }
+
+
+    /**
+     * Get the DPID of the router with node ID IF the node ID is the neighbor of the
+     * Switch srcSW.
+     * If the nodeId is the adjacency Sid, then it returns the destination router DPIDs.
+     *
+     * @param nodeId Node ID to check
+     * @param srcSw target Switch
+     * @return List of DPID of nodeId, empty list if the nodeId is not the neighbor of srcSW
+     */
+    private List<Dpid> getDpidIfNeighborOf(String nodeId, Switch srcSw) {
+        List<Dpid> fwdSws = new ArrayList<Dpid>();
+        // if the nodeID is the adjacency ID, then we need to regard it as the
+        // neighbor node ID and need to return the destination router DPID(s)
+        if (srManager.isAdjacencySid(nodeId)) {
+            String srcNodeId = srManager.getMplsLabel(srcSw.getDpid().toString());
+            List<Integer> ports =
+                    srManager.getAdacencyPorts(Integer.parseInt(srcNodeId),
+                            Integer.parseInt(nodeId));
+
+            for (Integer port: ports) {
+                for (Link link: srcSw.getOutgoingLinks()) {
+                    if (link.getSrcPort().getPortNumber().value() == port) {
+                        fwdSws.add(link.getDstSwitch().getDpid());
+                    }
+                }
+            }
+        }
+        else {
+            List<Dpid> fwdSwDpids =
+                    srManager.getForwardingSwitchForNodeId(srcSw,nodeId);
+            if (fwdSwDpids == null || fwdSwDpids.isEmpty()) {
+                log.warn("There is no route from node {} to node {}",
+                        srcSw.getDpid(), nodeId);
+                return fwdSws;
+            }
+
+            for (Dpid dpid: fwdSwDpids) {
+                String id = srManager.getMplsLabel(dpid.toString()).toString();
+                if (id.equals(nodeId)) {
+                    fwdSws.add(dpid);
+                    break;
+                }
+            }
+        }
+
+        return fwdSws;
+    }
+
+
+
+    /**
+     * 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) {
+
+        List<Integer> ports =
+                srManager.getAdacencyPorts(Integer.valueOf(prevNodeId),
+                        Integer.valueOf(adjacencySid));
+
+        if (ports == null) {
+            log.warn("Cannot find ports for node ID {} and adjacencySID ",
+                    prevNodeId, adjacencySid);
+            return false;
+        }
+
+        for (Integer port: ports) {
+            Switch sw = srManager.getSwitchFromNodeId(prevNodeId);
+            for (Link link: sw.getOutgoingLinks()) {
+                if (link.getSrcPort().getPortNumber().value() == port) {
+                    String linkDstDpid = link.getDstPort().getDpid().toString();
+                    String linkDstId = srManager.getMplsLabel(linkDstDpid);
+                    if (linkDstId.equals(nodeId)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Get the destination Nodes of the adjacency Sid
+     *
+     * @param nodeId  node ID of the adjacency Sid
+     * @param adjacencySid  adjacency Sid
+     * @return List of Switch, empty list if not found
+     */
+    private List<Switch> getAdjacencyDestinationNode(String nodeId, String adjacencySid) {
+        List<Switch> dstSwList = new ArrayList<Switch>();
+
+        List<Integer> ports = srManager.getAdacencyPorts(Integer.valueOf(nodeId),
+                Integer.valueOf(adjacencySid));
+
+        Switch srcSw = srManager.getSwitchFromNodeId(nodeId);
+        for (Integer port: ports) {
+            for (Link link: srcSw.getOutgoingLinks()) {
+                if (link.getSrcPort().getPortNumber().value() == port) {
+                    dstSwList.add(link.getDstSwitch());
+                }
+            }
+        }
+
+        return dstSwList;
+
+    }
+
+
+
+
+    /**
+     * print tunnel info - used only for debugging.
+     * @param targetSw
+     *
+     * @param fwdSwDpids
+     * @param ids
+     * @param tunnelId
+     */
+    private void printTunnelInfo(String targetSw, String tunnelId,
+            List<String> ids, NeighborSet ns) {
+        StringBuilder logStr = new StringBuilder("In switch " +
+                targetSw + ", create a tunnel " + tunnelId + " " + " of push ");
+        for (String id: ids)
+            logStr.append(id + "-");
+        logStr.append(" output to ");
+        for (Dpid dpid: ns.getDpids())
+            logStr.append(dpid + " - ");
+
+        log.debug(logStr.toString());
+
+    }
+
+    public class TunnelRouteInfo {
+
+        private String srcSwDpid;
+        private List<Dpid> fwdSwDpids;
+        private List<String> route;
+        private int gropuId;
+
+        public TunnelRouteInfo() {
+            fwdSwDpids = new ArrayList<Dpid>();
+            route = new ArrayList<String>();
+        }
+
+        private void setSrcDpid(String dpid) {
+            this.srcSwDpid = dpid;
+        }
+
+        private void setFwdSwDpid(List<Dpid> dpid) {
+            this.fwdSwDpids = dpid;
+        }
+
+        private void addRoute(String id) {
+            route.add(id);
+        }
+
+        private void setGroupId(int groupId) {
+            this.gropuId = groupId;
+        }
+
+        public String getSrcSwDpid() {
+            return this.srcSwDpid;
+        }
+
+        public List<Dpid> getFwdSwDpid() {
+            return this.fwdSwDpids;
+        }
+
+        public List<String> getRoute() {
+            return this.route;
+        }
+
+        public int getGroupId() {
+            return this.gropuId;
+        }
+    }
+
+}