From 903038e66ee3680151b8b8fd8db1b5af5a71607e Mon Sep 17 00:00:00 2001
From: Geddings Barrineau <cbarrin@g.clemson.edu>
Date: Tue, 12 Jul 2016 16:49:29 -0400
Subject: [PATCH] Transferred over all of the Yen's algorithm additions. This
 includes getRoutes(). Some additions may have been missed.

---
 .../routing/IRoutingService.java              |   3 +
 .../floodlightcontroller/routing/Route.java   |  21 +-
 .../routing/web/RouteSerializer.java          |  54 +++
 .../statistics/IStatisticsService.java        |   9 +-
 .../statistics/StatisticsCollector.java       |  49 +--
 .../topology/ITopologyService.java            |   7 +-
 .../topology/TopologyInstance.java            | 406 ++++++++++++++++++
 .../topology/TopologyManager.java             |  59 +++
 .../topology/web/RouteMetrics.java            |  74 ++++
 .../topology/web/RoutesResource.java          |  76 ++++
 .../topology/web/TopologyWebRoutable.java     |   2 +
 .../resources/floodlightdefault.properties    |   3 +-
 12 files changed, 724 insertions(+), 39 deletions(-)
 create mode 100644 src/main/java/net/floodlightcontroller/routing/web/RouteSerializer.java
 create mode 100644 src/main/java/net/floodlightcontroller/topology/web/RouteMetrics.java
 create mode 100644 src/main/java/net/floodlightcontroller/topology/web/RoutesResource.java

diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java
index fde607b89..cc9c41451 100644
--- a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java
+++ b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java
@@ -73,6 +73,9 @@ public interface IRoutingService extends IFloodlightService {
     /** return all routes, if available */
     public ArrayList<Route> getRoutes(DatapathId longSrcDpid, DatapathId longDstDpid, boolean tunnelEnabled);
 
+    /** Another version of getRoutes that uses Yen's algorithm under the hood. */
+    public ArrayList<Route> getRoutes(DatapathId srcDpid, DatapathId dstDpid, Integer numOfRoutesToGet);
+
     /** Check if a route exists between src and dst, including tunnel links
      *  in the path.
      */
diff --git a/src/main/java/net/floodlightcontroller/routing/Route.java b/src/main/java/net/floodlightcontroller/routing/Route.java
index 13f626692..13886c69b 100755
--- a/src/main/java/net/floodlightcontroller/routing/Route.java
+++ b/src/main/java/net/floodlightcontroller/routing/Route.java
@@ -17,22 +17,27 @@
 
 package net.floodlightcontroller.routing;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import net.floodlightcontroller.routing.web.RouteSerializer;
+import net.floodlightcontroller.core.types.NodePortTuple;
 import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.U64;
 
-import net.floodlightcontroller.core.types.NodePortTuple;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Represents a route between two switches
  *
  * @author David Erickson (daviderickson@cs.stanford.edu)
  */
+@JsonSerialize(using=RouteSerializer.class)
 public class Route implements Comparable<Route> {
     protected RouteId id;
     protected List<NodePortTuple> switchPorts;
     protected int routeCount;
+    protected int routeHopCount;
+    protected U64 routeLatency;
 
     public Route(RouteId id, List<NodePortTuple> switchPorts) {
         super();
@@ -89,6 +94,14 @@ public class Route implements Comparable<Route> {
     public int getRouteCount() {
         return routeCount;
     }
+
+    public void setRouteHopCount(int hopCount) { this.routeHopCount = hopCount; }
+
+    public int getRouteHopCount() { return this.routeHopCount; }
+
+    public void setRouteLatency(U64 latency) { this.routeLatency = latency; }
+
+    public U64 getRouteLatency() { return this.routeLatency; }
     
     @Override
     public int hashCode() {
diff --git a/src/main/java/net/floodlightcontroller/routing/web/RouteSerializer.java b/src/main/java/net/floodlightcontroller/routing/web/RouteSerializer.java
new file mode 100644
index 000000000..d9732eb79
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/routing/web/RouteSerializer.java
@@ -0,0 +1,54 @@
+/**
+ *    Copyright 2011,2012 Big Switch Networks, Inc. 
+ *    Originally created by David Erickson, Stanford University
+ *
+ *    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 net.floodlightcontroller.routing.web;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import net.floodlightcontroller.routing.Route;
+import net.floodlightcontroller.core.types.NodePortTuple;
+
+import java.io.IOException;
+
+public class RouteSerializer extends JsonSerializer<Route> {
+
+    @Override
+    public void serialize(Route route, JsonGenerator jGen, SerializerProvider serializer)
+            throws IOException, JsonProcessingException {
+        jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+
+        jGen.writeStartObject();
+        jGen.writeStringField("src_dpid", route.getId().getSrc().toString());
+        jGen.writeStringField("dst_dpid", route.getId().getDst().toString());
+        jGen.writeStringField("hop_count", Integer.toString(route.getRouteHopCount()));
+        jGen.writeNumberField("latency", route.getRouteLatency().getValue()); // Might be an issue if value exceed what unsigned long can hold
+        jGen.writeNumberField("route_count", route.getRouteCount());
+        jGen.writeFieldName("path");
+        jGen.writeStartArray();
+        for (NodePortTuple npt : route.getPath()) {
+            jGen.writeStartObject();
+            jGen.writeStringField("dpid", npt.getNodeId().toString());
+            jGen.writeNumberField("port", npt.getPortId().getPortNumber());
+            jGen.writeEndObject();
+        }
+        jGen.writeEndArray();
+        jGen.writeEndObject();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java
index 884e163bc..7cc047233 100644
--- a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java
+++ b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java
@@ -1,18 +1,19 @@
 package net.floodlightcontroller.statistics;
 
-import java.util.Map;
-
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.types.NodePortTuple;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
 
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.core.types.NodePortTuple;
+import java.util.Map;
 
 public interface IStatisticsService extends IFloodlightService {
 
 	public SwitchPortBandwidth getBandwidthConsumption(DatapathId dpid, OFPort p);
 		
 	public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption();
+
+	public long getLinkSpeed(NodePortTuple npt);
 	
 	public void collectStatistics(boolean collect);
 }
diff --git a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java
index fca59051d..50f441bd5 100644
--- a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java
+++ b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java
@@ -1,35 +1,7 @@
 package net.floodlightcontroller.statistics;
 
-import java.lang.Thread.State;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
-import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
-import org.projectfloodlight.openflow.protocol.OFStatsReply;
-import org.projectfloodlight.openflow.protocol.OFStatsRequest;
-import org.projectfloodlight.openflow.protocol.OFStatsType;
-import org.projectfloodlight.openflow.protocol.OFVersion;
-import org.projectfloodlight.openflow.protocol.match.Match;
-import org.projectfloodlight.openflow.protocol.ver13.OFMeterSerializerVer13;
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.TableId;
-import org.projectfloodlight.openflow.types.U64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import com.google.common.primitives.UnsignedLong;
 import com.google.common.util.concurrent.ListenableFuture;
-
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.internal.IOFSwitchService;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -40,6 +12,21 @@ import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.statistics.web.SwitchStatisticsWebRoutable;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+import org.projectfloodlight.openflow.protocol.*;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.ver13.OFMeterSerializerVer13;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.Thread.State;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
 public class StatisticsCollector implements IFloodlightModule, IStatisticsService {
 	private static final Logger log = LoggerFactory.getLogger(StatisticsCollector.class);
@@ -254,7 +241,11 @@ public class StatisticsCollector implements IFloodlightModule, IStatisticsServic
 	public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption() {
 		return Collections.unmodifiableMap(portStats);
 	}
-	
+
+	public long getLinkSpeed(NodePortTuple npt) {
+		return portStats.get(npt).getLinkSpeedBitsPerSec().getValue();
+	}
+
 	@Override
 	public synchronized void collectStatistics(boolean collect) {
 		if (collect && !isEnabled) {
diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
index e687ecf25..1aef705d6 100644
--- a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
+++ b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
@@ -27,7 +27,12 @@ import java.util.Map;
 import java.util.Set;
 
 public interface ITopologyService extends IFloodlightService  {
-	
+
+	public enum ROUTE_METRIC { LATENCY, HOPCOUNT, HOPCOUNT_AVOID_TUNNELS, UTILIZATION, LINK_SPEED };
+
+	public ROUTE_METRIC setRouteMetric(ROUTE_METRIC metric);
+	public ROUTE_METRIC getRouteMetric();
+
 	/*******************************************************
 	 * GENERAL TOPOLOGY FUNCTIONS
 	 *******************************************************/
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
index 76b8af2e7..cf3334ddb 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
@@ -740,6 +740,112 @@ public class TopologyInstance {
 
 		return ret;
 	}
+
+    /*
+	 * Creates a map of links and the cost associated with each link
+	 *
+	 *
+	 */
+
+    protected Map<Link,Integer> initLinkCostMap() {
+        Map<Link, Integer> linkCost = new HashMap<Link, Integer>();
+        long rawLinkSpeed;
+        int linkSpeedMBps;
+        int tunnel_weight = switchPorts.size() + 1;
+
+    		/* routeMetrics:
+        	 *  1: Hop Count(Default Metrics)
+         	 *  2: Hop Count (Treat Tunnels same as link)
+         	 *  3: Latency
+         	 *  4: Link Speed (Needs to be tested)
+         	*/
+        switch (TopologyManager.getRouteMetricInternal()){
+            case HOPCOUNT_AVOID_TUNNELS:
+                if(TopologyManager.collectStatistics == true){
+                    TopologyManager.statisticsService.collectStatistics(false);
+                    TopologyManager.collectStatistics = false;
+                }
+                log.info("Using Default: Hop Count with Tunnel Bias for Metrics");
+                for (NodePortTuple npt : tunnelPorts) {
+                    if (allLinks.get(npt) == null) continue;
+                    for (Link link : allLinks.get(npt)) {
+                        if (link == null) continue;
+                        linkCost.put(link, tunnel_weight);
+                    }
+                }
+                return linkCost;
+
+            case HOPCOUNT:
+                if(TopologyManager.collectStatistics == true){
+                    TopologyManager.statisticsService.collectStatistics(false);
+                    TopologyManager.collectStatistics = false;
+                }
+                log.info("Using Hop Count without Tunnel Bias for Metrics");
+                for (NodePortTuple npt : allLinks.keySet()) {
+                    if (allLinks.get(npt) == null) continue;
+                    for (Link link : allLinks.get(npt)) {
+                        if (link == null) continue;
+                        linkCost.put(link,1);
+                    }
+                }
+                return linkCost;
+
+            case LATENCY:
+                if(TopologyManager.collectStatistics == true){
+                    TopologyManager.statisticsService.collectStatistics(false);
+                    TopologyManager.collectStatistics = false;
+                }
+                log.info("Using Latency for Route Metrics");
+                for (NodePortTuple npt : allLinks.keySet()) {
+                    if (allLinks.get(npt) == null) continue;
+                    for (Link link : allLinks.get(npt)) {
+                        if (link == null) continue;
+                        if((int)link.getLatency().getValue() < 0 || (int)link.getLatency().getValue() > MAX_LINK_WEIGHT)
+                            linkCost.put(link, MAX_LINK_WEIGHT);
+                        else
+                            linkCost.put(link,(int)link.getLatency().getValue());
+                    }
+                }
+                return linkCost;
+
+            case LINK_SPEED:
+                if(TopologyManager.collectStatistics == false){
+                    TopologyManager.statisticsService.collectStatistics(true);
+                    TopologyManager.collectStatistics = true;
+                }
+                log.info("Using Link Speed for Route Metrics");
+                for (NodePortTuple npt : allLinks.keySet()) {
+                    if (allLinks.get(npt) == null) continue;
+                    rawLinkSpeed = TopologyManager.statisticsService.getLinkSpeed(npt);
+                    for (Link link : allLinks.get(npt)) {
+                        if (link == null) continue;
+
+                        if((rawLinkSpeed / 10^6) / 8 > 1){
+                            linkSpeedMBps = (int)(rawLinkSpeed / 10^6) / 8;
+                            linkCost.put(link, (1/linkSpeedMBps)*1000);
+                        }
+                        else
+                            linkCost.put(link, MAX_LINK_WEIGHT);
+                    }
+                }
+                return linkCost;
+
+            default:
+                if(TopologyManager.collectStatistics == true){
+                    TopologyManager.statisticsService.collectStatistics(false);
+                    TopologyManager.collectStatistics = false;
+                }
+                log.info("Invalid Selection: Using Default Hop Count with Tunnel Bias for Metrics");
+                for (NodePortTuple npt : tunnelPorts) {
+                    if (allLinks.get(npt) == null) continue;
+                    for (Link link : allLinks.get(npt)) {
+                        if (link == null) continue;
+                        linkCost.put(link, tunnel_weight);
+                    }
+                }
+                return linkCost;
+        }
+    }
 	
     /*
 	 * Modification of the calculateShortestPathTreeInClusters (dealing with whole topology, not individual clusters)
@@ -924,6 +1030,52 @@ public class TopologyInstance {
         return result;
     }
 
+    protected Route buildroute(RouteId id, BroadcastTree tree) {
+        NodePortTuple npt;
+        DatapathId srcId = id.getSrc();
+        DatapathId dstId = id.getDst();
+        //set of NodePortTuples on the route
+        LinkedList<NodePortTuple> sPorts = new LinkedList<NodePortTuple>();
+
+        if (tree == null) return null;
+
+        // TODO: Check if the src and dst are in the tree
+        // if (destinationRootedFullTrees.get(dstId) == null) return null;
+
+        Map<DatapathId, Link> nexthoplinks = tree.getLinks();
+
+        if (!switches.contains(srcId) || !switches.contains(dstId)) {
+            // This is a switch that is not connected to any other switch
+            // hence there was no update for links (and hence it is not
+            // in the network)
+            log.info("buildroute: Standalone switch: {}", srcId);
+
+            // The only possible non-null path for this case is
+            // if srcId equals dstId --- and that too is an 'empty' path []
+
+        } else if ((nexthoplinks!=null) && (nexthoplinks.get(srcId) != null)) {
+            while (!srcId.equals(dstId)) {
+                Link l = nexthoplinks.get(srcId);
+                npt = new NodePortTuple(l.getSrc(), l.getSrcPort());
+                sPorts.addLast(npt);
+                npt = new NodePortTuple(l.getDst(), l.getDstPort());
+                sPorts.addLast(npt);
+                srcId = nexthoplinks.get(srcId).getDst();
+            }
+        }
+        // else, no path exists, and path equals null
+
+        Route result = null;
+        if (sPorts != null && !sPorts.isEmpty()) {
+            result = new Route(id, sPorts);
+
+        }
+        if (log.isTraceEnabled()) {
+            log.trace("buildroute: {}", result);
+        }
+        return result;
+    }
+
     /*
      * Getter Functions
      */
@@ -946,6 +1098,260 @@ public class TopologyInstance {
         return true;
     }
 
+    /*
+    Function that calls Yen's algorithm and returns a list of routes
+    from A to B.
+	 */
+    protected ArrayList<Route> getRoutes(DatapathId src, DatapathId dst, Integer K) {
+        return yens(src, dst, K);
+    }
+
+    protected Map<DatapathId, Set<Link>> buildLinkDpidMap(Set<DatapathId> switches, Map<DatapathId,
+            Set<OFPort>> switchPorts, Map<NodePortTuple, Set<Link>> allLinks) {
+
+        Map<DatapathId, Set<Link>> linkDpidMap = new HashMap<DatapathId, Set<Link>>();
+        for (DatapathId s : switches) {
+            if (switchPorts.get(s) == null) continue;
+            for (OFPort p : switchPorts.get(s)) {
+                NodePortTuple np = new NodePortTuple(s, p);
+                if (allLinks.get(np) == null) continue;
+                for (Link l : allLinks.get(np)) {
+                    if (switches.contains(l.getSrc()) && switches.contains(l.getDst())) {
+                        if (linkDpidMap.containsKey(s)) {
+                            linkDpidMap.get(s).add(l);
+                        } else {
+                            linkDpidMap.put(s, new HashSet<Link>(Arrays.asList(l)));
+                        }
+                    }
+                }
+            }
+        }
+
+        return linkDpidMap;
+    }
+
+    protected void setRouteCosts(Route r) {
+        U64 cost = U64.ZERO;
+
+        // Set number of hops. Assuming the list of NPTs is always even.
+        r.setRouteHopCount(r.getPath().size()/2);
+
+        for (int i = 0; i <= r.getPath().size() - 2; i = i + 2) {
+            DatapathId src = r.getPath().get(i).getNodeId();
+            DatapathId dst = r.getPath().get(i + 1).getNodeId();
+            OFPort srcPort = r.getPath().get(i).getPortId();
+            OFPort dstPort = r.getPath().get(i + 1).getPortId();
+            for (Link l : allLinks.get(r.getPath().get(i))) {
+                //log.debug("Iterating through the links");
+                if (l.getSrc().equals(src) && l.getDst().equals(dst) &&
+                        l.getSrcPort().equals(srcPort) && l.getDstPort().equals(dstPort)) {
+                    log.info("Matching link found: {}", l);
+                    cost = cost.add(l.getLatency());
+                }
+            }
+        }
+
+        r.setRouteLatency(cost);
+        log.info("Total cost is {}", cost);
+        log.info(r.toString());
+
+    }
+
+    protected ArrayList<Route> yens(DatapathId src, DatapathId dst, Integer K) {
+
+        //log.debug("YENS ALGORITHM -----------------");
+        //log.debug("Asking for routes from {} to {}", src, dst);
+        //log.debug("Asking for {} routes", K);
+
+        // Find link costs
+        Map<Link, Integer> linkCost = initLinkCostMap();
+
+        Map<DatapathId, Set<Link>> linkDpidMap = buildLinkDpidMap(switches, switchPorts, allLinks);
+
+        Map<DatapathId, Set<Link>> copyOfLinkDpidMap = new HashMap<DatapathId, Set<Link>>(linkDpidMap);
+
+        // A is the list of shortest paths. The number in the list at the end should be less than or equal to K
+        // B is the list of possible shortest paths found in this function.
+        ArrayList<Route> A = new ArrayList<Route>();
+        ArrayList<Route> B = new ArrayList<Route>();
+
+        // The number of routes requested should never be less than 1.
+        if (K < 1) {
+            return A;
+        }
+
+        if (!switches.contains(src) || !switches.contains(dst)) {
+            return A;
+        }
+
+        // Using Dijkstra's to find the shortest path, which will also be the first path in A
+        Route newroute = buildroute(new RouteId(src, dst), dijkstra(copyOfLinkDpidMap, dst, linkCost, true));
+
+        if (newroute != null) {
+            setRouteCosts(newroute);
+            A.add(newroute);
+        }
+        else {
+            log.info("No routes found in Yen's!");
+            return A;
+        }
+
+        // Loop through K - 1 times to get other possible shortest paths
+        for (int k = 1; k < K; k++) {
+            log.info("k: {}", k);
+            //log.debug("Path Length 'A.get(k-1).getPath().size()-2': {}", A.get(k - 1).getPath().size() - 2);
+            // Iterate through i, which is the number of links in the most recent path added to A
+            for (int i = 0; i <= A.get(k - 1).getPath().size() - 2; i = i + 2) {
+                log.info("i: {}", i);
+                List<NodePortTuple> path = A.get(k - 1).getPath();
+                //log.debug("A(k-1): {}", A.get(k - 1).getPath());
+                // The spur node is the point in the topology where Dijkstra's is called again to find another path
+                DatapathId spurNode = path.get(i).getNodeId();
+                // rootPath is the path along the previous shortest path that is before the spur node
+                Route rootPath = new Route(new RouteId(path.get(0).getNodeId(), path.get(i).getNodeId()),
+                        path.subList(0, i));
+
+
+                Map<NodePortTuple, Set<Link>> allLinksCopy = new HashMap<NodePortTuple, Set<Link>>(allLinks);
+                // Remove the links after the spur node that are part of other paths in A so that new paths
+                // found are unique
+                for (Route r : A) {
+                    if (r.getPath().size() > (i + 1) && r.getPath().subList(0, i).equals(rootPath.getPath())) {
+                        allLinksCopy.remove(r.getPath().get(i));
+                        allLinksCopy.remove(r.getPath().get(i+1));
+                    }
+                }
+
+                // Removes the root path so Dijkstra's doesn't try to go through it to find a path
+                Set<DatapathId> switchesCopy = new HashSet<DatapathId>(switches);
+                for (NodePortTuple npt : rootPath.getPath()) {
+                    if (!npt.getNodeId().equals(spurNode)) {
+                        switchesCopy.remove(npt.getNodeId());
+                    }
+                }
+
+                // Builds the new topology without the parts we want removed
+                copyOfLinkDpidMap = buildLinkDpidMap(switchesCopy, switchPorts, allLinksCopy);
+
+                //log.debug("About to build route.");
+                //log.debug("Switches: {}", switchesCopy);
+                // Uses Dijkstra's to try to find a shortest path from the spur node to the destination
+                Route spurPath = buildroute(new RouteId(spurNode, dst), dijkstra(copyOfLinkDpidMap, dst, linkCost, true));
+                if (spurPath == null) {
+                    //log.debug("spurPath is null");
+                    continue;
+                }
+
+                // Adds the root path and spur path together to get a possible shortest path
+                List<NodePortTuple> totalNpt = new LinkedList<NodePortTuple>();
+                totalNpt.addAll(rootPath.getPath());
+                totalNpt.addAll(spurPath.getPath());
+                Route totalPath = new Route(new RouteId(src, dst), totalNpt);
+                setRouteCosts(totalPath);
+
+                log.info("Spur Node: {}", spurNode);
+                log.info("Root Path: {}", rootPath);
+                log.info("Spur Path: {}", spurPath);
+                log.info("Total Path: {}", totalPath);
+                // Adds the new path into B
+                int flag = 0;
+                for (Route r_B : B) {
+                    for (Route r_A : A) {
+                        if (r_B.getPath().equals(totalPath.getPath()) || r_A.getPath().equals(totalPath.getPath())) {
+                            flag = 1;
+                        }
+                    }
+                }
+                if (flag == 0) {
+                    B.add(totalPath);
+                }
+
+
+
+
+                // Restore edges and nodes to graph
+            }
+
+            // If we get out of the loop and there isn't a path in B to add to A, all possible paths have been
+            // found and return A
+            if (B.isEmpty()) {
+                //log.debug("B list is empty in Yen's");
+                break;
+            }
+
+            //log.debug("Removing shortest path from {}", B);
+            // Find the shortest path in B, remove it, and put it in A
+            log.info("--------------BEFORE------------------------");
+            for (Route r : B) {
+                log.info(r.toString());
+            }
+            log.info("--------------------------------------------");
+            Route shortestPath = removeShortestPath(B, linkCost);
+            log.info("--------------AFTER------------------------");
+            for (Route r : B) {
+                log.info(r.toString());
+            }
+            log.info("--------------------------------------------");
+
+            if (shortestPath != null) {
+                //log.debug("Adding new shortest path to {} in Yen's", shortestPath);
+                A.add(shortestPath);
+                log.info("A: {}", A);
+            }
+            else {
+                //log.debug("removeShortestPath returned {}", shortestPath);
+            }
+        }
+
+        // Set the route counts
+        for (Route r : A) {
+            r.setRouteCount(A.indexOf(r));
+        }
+        //log.debug("END OF YEN'S --------------------");
+        return A;
+    }
+
+    protected Route removeShortestPath(ArrayList<Route> routes, Map<Link, Integer> linkCost) {
+        log.debug("REMOVE SHORTEST PATH -------------");
+        // If there is nothing in B, return
+        if(routes == null){
+            log.debug("Routes == null");
+            return null;
+        }
+        Route shortestPath = null;
+        // Set the default shortest path to the max value
+        Integer shortestPathCost = Integer.MAX_VALUE;
+
+        // Iterate through B and find the shortest path
+        for (Route r : routes) {
+            Integer pathCost = 0;
+            // Add up the weights of each link in the path
+            // TODO Get the path cost from the route object
+            for (NodePortTuple npt : r.getPath()) {
+                if (allLinks.get(npt) ==  null || linkCost.get(allLinks.get(npt).iterator().next()) == null) {
+                    pathCost++;
+                }
+                else {
+                    pathCost += linkCost.get(allLinks.get(npt).iterator().next());
+                }
+            }
+            log.debug("Path {} with cost {}", r, pathCost);
+            // If it is smaller than the current smallest, replace variables with the path just found
+            if (pathCost < shortestPathCost) {
+                log.debug("New shortest path {} with cost {}", r, pathCost);
+                shortestPathCost = pathCost;
+                shortestPath = r;
+            }
+        }
+
+        log.debug("Remove {} from {}", shortestPath, routes);
+        // Remove the route from B and return it
+        routes.remove(shortestPath);
+
+        log.debug("Shortest path: {}", shortestPath);
+        return shortestPath;
+    }
+
 	/*
 	* Calculates E2E route
 	*/
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index 648910916..f89d9d014 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -35,6 +35,7 @@ import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.routing.Route;
+import net.floodlightcontroller.statistics.IStatisticsService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.topology.web.TopologyWebRoutable;
 import org.projectfloodlight.openflow.protocol.*;
@@ -62,6 +63,11 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 	protected static Logger log = LoggerFactory.getLogger(TopologyManager.class);
 	public static final String MODULE_NAME = "topology";
 
+	protected static IStatisticsService statisticsService;
+
+	protected static volatile ROUTE_METRIC routeMetric = ROUTE_METRIC.HOPCOUNT_AVOID_TUNNELS; //default: route on hop count
+	protected static boolean collectStatistics = false;
+
 	/**
 	 * Role of the controller.
 	 */
@@ -325,6 +331,21 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 		return ti.isAllowed(sw, portId);
 	}
 
+	@Override
+	public ROUTE_METRIC setRouteMetric(ROUTE_METRIC metric) {
+		routeMetric = metric;
+		return routeMetric;
+	}
+
+	@Override
+	public ROUTE_METRIC getRouteMetric() {
+		return routeMetric;
+	}
+
+	static protected ROUTE_METRIC getRouteMetricInternal() {
+		return routeMetric;
+	}
+
 	////////////////////////////////////////////////////////////////////////
 	////////////////////////////////////////////////////////////////////////
 	@Override
@@ -606,6 +627,15 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 		return result;
 	}
 
+	public ArrayList<Route> getRoutes(DatapathId srcDpid, DatapathId dstDpid, Integer k) {
+		return getCurrentInstance().getRoutes(srcDpid, dstDpid, k);
+	}
+
+	public Map<Link, Integer> getLinkCostMap(boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.initLinkCostMap();
+	}
+
 	// ******************
 	// IOFMessageListener
 	// ******************
@@ -734,6 +764,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 		switchService = context.getServiceImpl(IOFSwitchService.class);
 		restApiService = context.getServiceImpl(IRestApiService.class);
 		debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+		statisticsService = context.getServiceImpl(IStatisticsService.class);
 
 		switchPorts = new HashMap<DatapathId, Set<OFPort>>();
 		switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
@@ -744,6 +775,34 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 		ldUpdates = new LinkedBlockingQueue<LDUpdate>();
 		haListener = new HAListenerDelegate();
 		registerTopologyDebugCounters();
+
+		Map<String, String> configOptions = context.getConfigParams(this);
+		String metric = configOptions.get("routeMetric") != null
+				? configOptions.get("routeMetric").trim().toLowerCase() : null;
+		if (metric != null) {
+			switch (metric) {
+				case "latency":
+					routeMetric = ROUTE_METRIC.LATENCY;
+					break;
+				case "utilization":
+					routeMetric = ROUTE_METRIC.UTILIZATION;
+					break;
+				case "hopcount":
+					routeMetric = ROUTE_METRIC.HOPCOUNT;
+					break;
+				case "hopcount_avoid_tunnels":
+					routeMetric = ROUTE_METRIC.HOPCOUNT_AVOID_TUNNELS;
+					break;
+				case "link_speed":
+					routeMetric = ROUTE_METRIC.LINK_SPEED;
+					break;
+				default:
+					log.error("Invalid input {}", metric);
+					break;
+			}
+		}
+
+		log.info("Route Metrics set to {}", routeMetric);
 	}
 
 	@Override
diff --git a/src/main/java/net/floodlightcontroller/topology/web/RouteMetrics.java b/src/main/java/net/floodlightcontroller/topology/web/RouteMetrics.java
new file mode 100644
index 000000000..4c4112a88
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/topology/web/RouteMetrics.java
@@ -0,0 +1,74 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    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 net.floodlightcontroller.topology.web;
+
+import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.topology.ITopologyService.ROUTE_METRIC;
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class RouteMetrics extends ServerResource {
+    private static final Logger log = LoggerFactory.getLogger(RouteMetrics.class);
+
+    @Put
+    @Post
+    public Map<String, String> changeMetric() {
+        ITopologyService topology =
+                (ITopologyService)getContext().getAttributes().
+                        get(ITopologyService.class.getCanonicalName());
+
+        String metric = (String) getRequestAttributes().get("metric");
+        metric = metric.trim().toLowerCase();
+
+        ROUTE_METRIC type;
+
+        switch (metric) {
+            case "latency":
+                type = ROUTE_METRIC.LATENCY;
+                break;
+            case "utilization":
+                type = ROUTE_METRIC.UTILIZATION;
+                break;
+            case "hopcount":
+                type = ROUTE_METRIC.HOPCOUNT;
+                break;
+            case "hopcount_avoid_tunnels":
+                type = ROUTE_METRIC.HOPCOUNT_AVOID_TUNNELS;
+                break;
+            case "link_speed":
+                type = ROUTE_METRIC.LINK_SPEED;
+                break;
+            default:
+                log.error("Invalid input {}", metric);
+                return Collections.singletonMap("error", "invalid route metric " + metric);
+        }
+
+        if (topology.setRouteMetric(type) != type) {
+            log.error("Failed to set valid route metric {}. Bug?", metric);
+            return Collections.singletonMap("error", "failed to set valid route metric " + metric);
+        }
+
+        log.debug("Set route metric to {}", metric);
+        return Collections.singletonMap("success", "route metric set to " + metric);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/topology/web/RoutesResource.java b/src/main/java/net/floodlightcontroller/topology/web/RoutesResource.java
new file mode 100644
index 000000000..902ad33b9
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/topology/web/RoutesResource.java
@@ -0,0 +1,76 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    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 net.floodlightcontroller.topology.web;
+
+import net.floodlightcontroller.routing.IRoutingService;
+import net.floodlightcontroller.routing.Route;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.List;
+
+public class RoutesResource extends ServerResource {
+
+    protected static Logger log = LoggerFactory.getLogger(RoutesResource.class);
+
+    @Get("json")
+    public List<Route> retrieve() {
+        IRoutingService routing =
+                (IRoutingService)getContext().getAttributes().
+                        get(IRoutingService.class.getCanonicalName());
+
+        String srcDpid = (String) getRequestAttributes().get("src-dpid");
+        String dstDpid = (String) getRequestAttributes().get("dst-dpid");
+        Integer numRoutes = Integer.parseInt((String) getRequestAttributes().get("num-routes"));
+
+        log.debug("Asking for routes from {} to {}", srcDpid, dstDpid);
+        log.debug("Asking for {} routes", numRoutes);
+
+        DatapathId longSrcDpid = DatapathId.of(srcDpid);
+        DatapathId longDstDpid = DatapathId.of(dstDpid);
+
+        List<Route> results = null;
+        try {
+            results = routing.getRoutes(longSrcDpid, longDstDpid, numRoutes);
+        } catch (Exception e) {
+            log.warn("{}", e);
+            log.warn("EXCEPTION: No routes found in request for routes from {} to {}", srcDpid, dstDpid);
+        }
+
+        if (results == null || results.isEmpty()) {
+            log.warn("No routes found in request for routes from {} to {}", srcDpid, dstDpid);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("Got {} routes from {} to {}", new Object[] { results.size(), srcDpid, dstDpid });
+                log.debug("These are the routes ---------------------------");
+                log.debug("{}", results);
+                log.debug("------------------------------------------------");
+            }
+
+            if (results.size() > 0 && results.contains(null)) {
+                log.error("Geddings, Junaid, Scott, etc., how is this happening if there should be no routes? I tested b/t 2 non-existing switches and using the same switch as src and dst.");
+            } else {
+                return results;
+            }
+        }
+        return Collections.emptyList();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/topology/web/TopologyWebRoutable.java b/src/main/java/net/floodlightcontroller/topology/web/TopologyWebRoutable.java
index 0ae6a09f8..431926316 100644
--- a/src/main/java/net/floodlightcontroller/topology/web/TopologyWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/topology/web/TopologyWebRoutable.java
@@ -40,6 +40,8 @@ public class TopologyWebRoutable implements RestletRoutable {
         router.attach("/enabledports/json", EnabledPortsResource.class);
         router.attach("/blockedports/json", BlockedPortsResource.class);
         router.attach("/route/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", RouteResource.class);
+        router.attach("/routes/{src-dpid}/{dst-dpid}/{num-routes}/json", RoutesResource.class);
+        router.attach("/setroutemetric/{metric}/json", RouteMetrics.class);
 
         return router;
     }
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index 31a9dead4..4926ac812 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -62,4 +62,5 @@ net.floodlightcontroller.restserver.RestApiServer.httpsPort=8081
 net.floodlightcontroller.restserver.RestApiServer.httpPort=8080
 net.floodlightcontroller.restserver.RestApiServer.accessControlAllowAllOrigins=TRUE
 net.floodlightcontroller.statistics.StatisticsCollector.enable=FALSE
-net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10
\ No newline at end of file
+net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10
+net.floodlightcontroller.topology.TopologyManager.routeMetric=latency
\ No newline at end of file
-- 
GitLab