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