diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java index a2af40ba7745c1f48de3656ff4199409a2f6c5ab..856ec0bda2267604688621387742e211d090793d 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java @@ -173,4 +173,20 @@ public interface IRoutingService extends IFloodlightService { * @return list of paths ordered least to greatest cost */ public List<Path> getPathsSlow(DatapathId src, DatapathId dst, int numReqPaths); + + /** + * Recompute paths now, regardless of whether or not there was a change in the + * topology. This should be called if {@link #setPathMetric(PATH_METRIC)} was + * invoked to change the PATH_METRIC **and we want the new metric to take effect + * now** for future getPathsFast() or getPath() function calls. This will allow other + * modules using IRoutingService path-finding to use paths based on the new metric + * if the other modules only use the "fast" path-finding API. + * + * One can use {@link IRoutingService#getPathsSlow(DatapathId, DatapathId, int)} if there is no + * urgency for the new metric to take effect and yet one would still like to see + * the paths using the new metric once or so. In this case, one need not invoke {@link #forceRecompute()}. + * + * @return true upon success; false otherwise + */ + public boolean forceRecompute(); } diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingManager.java b/src/main/java/net/floodlightcontroller/routing/RoutingManager.java index 8268cee5395794637ac79b61d52d63913bac88d4..3110da7470d76371afd37d53c9350ac3def3b36f 100644 --- a/src/main/java/net/floodlightcontroller/routing/RoutingManager.java +++ b/src/main/java/net/floodlightcontroller/routing/RoutingManager.java @@ -122,6 +122,11 @@ public class RoutingManager implements IFloodlightModule, IRoutingService { public boolean pathExists(DatapathId src, DatapathId dst) { return tm.getCurrentTopologyInstance().pathExists(src, dst); } + + @Override + public boolean forceRecompute() { + return tm.forceRecompute(); + } /** * Registers an IRoutingDecisionChangedListener. @@ -157,5 +162,4 @@ public class RoutingManager implements IFloodlightModule, IRoutingService { listener.routingDecisionChanged(changedDecisions); } } - } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/web/ForceRecomputeResource.java b/src/main/java/net/floodlightcontroller/routing/web/ForceRecomputeResource.java new file mode 100644 index 0000000000000000000000000000000000000000..ce408644d53a60fb29ee78b2f63f126ffe103089 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/routing/web/ForceRecomputeResource.java @@ -0,0 +1,44 @@ +/** + * 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.routing.web; + +import net.floodlightcontroller.routing.IRoutingService; +import org.restlet.resource.Post; +import org.restlet.resource.Put; +import org.restlet.resource.ServerResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class ForceRecomputeResource extends ServerResource { + private static final Logger log = LoggerFactory.getLogger(ForceRecomputeResource.class); + + @Put + @Post + public Map<String, String> forceRecompute() { + IRoutingService routing = + (IRoutingService)getContext().getAttributes(). + get(IRoutingService.class.getCanonicalName()); + + boolean result = routing.forceRecompute(); + log.debug("Force recompute result {}", result); + return ImmutableMap.of("result", result ? "paths recomputed" : "error recomputing paths"); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/web/MaxFastPathsResource.java b/src/main/java/net/floodlightcontroller/routing/web/MaxFastPathsResource.java new file mode 100644 index 0000000000000000000000000000000000000000..1c0b193425827314a3652110388cf36f4f9ffd92 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/routing/web/MaxFastPathsResource.java @@ -0,0 +1,109 @@ +/** + * 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.routing.web; + +import net.floodlightcontroller.routing.IRoutingService; + +import org.restlet.resource.Get; +import org.restlet.resource.Post; +import org.restlet.resource.Put; +import org.restlet.resource.ServerResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.MappingJsonFactory; +import com.google.common.collect.ImmutableMap; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +public class MaxFastPathsResource extends ServerResource { + private static final Logger log = LoggerFactory.getLogger(MaxFastPathsResource.class); + + private static String maxPathsFromJson(String json) { + MappingJsonFactory f = new MappingJsonFactory(); + JsonParser jp; + String max = ""; + try { + try { + jp = f.createParser(json); + } catch (JsonParseException e) { + throw new IOException(e); + } + + jp.nextToken(); + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new IOException("Expected FIELD_NAME"); + } + + String n = jp.getCurrentName(); + jp.nextToken(); + if (jp.getText().equals("")) { + continue; + } + + if (n.equalsIgnoreCase("max_fast_paths")) { + max = jp.getText(); + } + } + } catch (IOException e) { + log.error("Unable to parse JSON string: {}", e); + } + return max.trim().toLowerCase(); + } + + @Put + @Post + public Map<String, String> changeMaxPathsToCompute(String json) { + IRoutingService routing = + (IRoutingService)getContext().getAttributes(). + get(IRoutingService.class.getCanonicalName()); + + int max = 0; + + try { + max = Integer.parseInt(maxPathsFromJson(json)); + if (max < 0) { + throw new NumberFormatException(); + } + } catch (NumberFormatException e) { + log.error("Could not parse max_fast_paths {}", max); + return Collections.singletonMap("error", "invalid max_fast_paths: " + max); + } + + log.debug("Setting max_fast_paths to {}", max); + routing.setMaxPathsToCompute(max); + return ImmutableMap.of("max_fast_paths", Integer.toString(routing.getMaxPathsToCompute())); + } + + @Get + public Map<String, String> getMaxPaths() { + IRoutingService routing = + (IRoutingService)getContext().getAttributes(). + get(IRoutingService.class.getCanonicalName()); + return ImmutableMap.of("max_fast_paths", Integer.toString(routing.getMaxPathsToCompute())); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java b/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java index cf032a135520f0395eefa7a405409f673af901d8..c9c51380abac5bdac6ada7545cdbea7fdd34bb6c 100644 --- a/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java +++ b/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java @@ -18,53 +18,101 @@ package net.floodlightcontroller.routing.web; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.IRoutingService.PATH_METRIC; + +import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.Put; import org.restlet.resource.ServerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.MappingJsonFactory; +import com.google.common.collect.ImmutableMap; + +import java.io.IOException; import java.util.Collections; import java.util.Map; public class PathMetricsResource extends ServerResource { private static final Logger log = LoggerFactory.getLogger(PathMetricsResource.class); + private static String metricFromJson(String json) { + MappingJsonFactory f = new MappingJsonFactory(); + JsonParser jp; + String metric = ""; + try { + try { + jp = f.createParser(json); + } catch (JsonParseException e) { + throw new IOException(e); + } + + jp.nextToken(); + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new IOException("Expected FIELD_NAME"); + } + + String n = jp.getCurrentName(); + jp.nextToken(); + if (jp.getText().equals("")) { + continue; + } + + if (n.equalsIgnoreCase("metric")) { + metric = jp.getText(); + } + } + } catch (IOException e) { + log.error("Unable to parse JSON string: {}", e); + } + return metric.trim().toLowerCase(); + } + @Put @Post - public Map<String, String> changeMetric() { + public Map<String, String> changeMetric(String json) { IRoutingService routing = (IRoutingService)getContext().getAttributes(). - get(IRoutingService.class.getCanonicalName()); + get(IRoutingService.class.getCanonicalName()); - String metric = (String) getRequestAttributes().get("metric"); - metric = metric.trim().toLowerCase(); + String metric = metricFromJson(json); PATH_METRIC type; - switch (metric) { - case "latency": - type = PATH_METRIC.LATENCY; - break; - case "utilization": - type = PATH_METRIC.UTILIZATION; - break; - case "hopcount": - type = PATH_METRIC.HOPCOUNT; - break; - case "hopcount_avoid_tunnels": - type = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; - break; - case "link_speed": - type = PATH_METRIC.LINK_SPEED; - break; - default: - log.error("Invalid input {}", metric); - return Collections.singletonMap("error", "invalid path metric " + metric); + if (metric.equals(PATH_METRIC.LATENCY.getMetricName())) { + type = PATH_METRIC.LATENCY; + } else if (metric.equals(PATH_METRIC.UTILIZATION.getMetricName())) { + type = PATH_METRIC.UTILIZATION; + } else if (metric.equals(PATH_METRIC.HOPCOUNT.getMetricName())) { + type = PATH_METRIC.HOPCOUNT; + } else if (metric.equals(PATH_METRIC.HOPCOUNT_AVOID_TUNNELS.getMetricName())) { + type = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; + } else if (metric.equals(PATH_METRIC.LINK_SPEED.getMetricName())) { + type = PATH_METRIC.LINK_SPEED; + } else { + log.error("Invalid input {}", metric); + return Collections.singletonMap("error", "invalid path metric: " + metric); } log.debug("Setting path metric to {}", type.getMetricName()); routing.setPathMetric(type); - return Collections.singletonMap("success", "path metric set to " + type.getMetricName()); + return Collections.singletonMap("metric", type.getMetricName()); + } + + @Get + public Map<String, String> getMetric() { + IRoutingService routing = + (IRoutingService)getContext().getAttributes(). + get(IRoutingService.class.getCanonicalName()); + PATH_METRIC metric = routing.getPathMetric(); + return ImmutableMap.of("metric", metric.getMetricName()); } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/web/RoutingWebRoutable.java b/src/main/java/net/floodlightcontroller/routing/web/RoutingWebRoutable.java index d9901f0fbb82a133030703847bddb88d60708345..b56a2a5b6f5f407b056284e997e89b8fad5b300e 100644 --- a/src/main/java/net/floodlightcontroller/routing/web/RoutingWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/routing/web/RoutingWebRoutable.java @@ -35,8 +35,9 @@ public class RoutingWebRoutable implements RestletRoutable { router.attach("/paths/{src-dpid}/{dst-dpid}/{num-paths}/json", PathsResource.class); router.attach("/paths/fast/{src-dpid}/{dst-dpid}/{num-paths}/json", PathsResource.class); router.attach("/paths/slow/{src-dpid}/{dst-dpid}/{num-paths}/json", PathsResource.class); - router.attach("/setpathmetric/{metric}/json", PathMetricsResource.class); - + router.attach("/metric/json", PathMetricsResource.class); + router.attach("/paths/force-recompute/json", ForceRecomputeResource.class); + router.attach("/paths/max-fast-paths/json", MaxFastPathsResource.class); return router; } diff --git a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java index 7cc047233312e8ca492b1556403ac7a811bd527f..9075d98d291a9a91776a7b5a84dbc965b0f09c12 100644 --- a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java +++ b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java @@ -12,8 +12,6 @@ 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 50f441bd52b3a61a6ad6c3fcb06361cd8f6ce7df..97bd54aef17338adefe770c0ae3dc16b5f0e8b13 100644 --- a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java +++ b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java @@ -242,10 +242,6 @@ public class StatisticsCollector implements IFloodlightModule, IStatisticsServic 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/ITopologyManagerBackend.java b/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java index 64c4e06a2abcf3581fa23e5a99a96503d92d727d..542a7728e4b42c2291ab8d8d36cce1b7abe69270 100644 --- a/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java +++ b/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java @@ -2,7 +2,7 @@ package net.floodlightcontroller.topology; import net.floodlightcontroller.routing.IRoutingService.PATH_METRIC; -public interface ITopologyManagerBackend { +public interface ITopologyManagerBackend extends ITopologyService { public TopologyInstance getCurrentTopologyInstance(); public PATH_METRIC getPathMetric(); @@ -10,4 +10,6 @@ public interface ITopologyManagerBackend { public int getMaxPathsToCompute(); public void setMaxPathsToCompute(int max); + + public boolean forceRecompute(); } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyPathFinding.java b/src/main/java/net/floodlightcontroller/topology/ITopologyPathFinding.java new file mode 100644 index 0000000000000000000000000000000000000000..3260f460650910f2331a6a1ff0095fc15e23efc4 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/topology/ITopologyPathFinding.java @@ -0,0 +1,7 @@ +package net.floodlightcontroller.topology; + +import net.floodlightcontroller.routing.IRoutingService; + +public interface ITopologyPathFinding extends IRoutingService { + +} diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index fbd6bf8a4208cfa2a554f59cd66fcc2d3341a0c1..80a41d494740494e665c69d651e4dd3eda4340e0 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -16,12 +16,16 @@ package net.floodlightcontroller.topology; +import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.types.NodePortTuple; import net.floodlightcontroller.linkdiscovery.Link; import net.floodlightcontroller.routing.BroadcastTree; import net.floodlightcontroller.routing.Path; import net.floodlightcontroller.routing.PathId; +import net.floodlightcontroller.statistics.SwitchPortBandwidth; import net.floodlightcontroller.util.ClusterDFS; + +import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.U64; @@ -643,92 +647,113 @@ public class TopologyInstance { */ public Map<Link,Integer> initLinkCostMap() { Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); - long rawLinkSpeed; - int linkSpeedMBps; int tunnel_weight = portsWithLinks.size() + 1; - /* pathMetrics: - * 1: Hop Count (Default Metrics) - * 2: Hop Count (Treat Tunnels same as link) - * 3: Latency - * 4: Link Speed - */ switch (TopologyManager.getPathMetricInternal()){ case HOPCOUNT_AVOID_TUNNELS: - if(TopologyManager.collectStatistics == true){ - TopologyManager.statisticsService.collectStatistics(false); - TopologyManager.collectStatistics = false; - } - log.debug("Using Default: Hop Count with Tunnel Bias for Metrics"); + log.debug("Using hop count with tunnel bias for metrics"); for (NodePortTuple npt : portsTunnel) { - if (links.get(npt) == null) continue; + if (links.get(npt) == null) { + continue; + } for (Link link : links.get(npt)) { - if (link == null) continue; + 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.debug("Using Hop Count without Tunnel Bias for Metrics"); + log.debug("Using hop count w/o tunnel bias for metrics"); for (NodePortTuple npt : links.keySet()) { - if (links.get(npt) == null) continue; + if (links.get(npt) == null) { + continue; + } for (Link link : links.get(npt)) { - if (link == null) continue; + if (link == null) { + continue; + } linkCost.put(link,1); } } return linkCost; case LATENCY: - if(TopologyManager.collectStatistics == true){ - TopologyManager.statisticsService.collectStatistics(false); - TopologyManager.collectStatistics = false; - } - log.debug("Using Latency for Route Metrics"); + log.debug("Using latency for path metrics"); for (NodePortTuple npt : links.keySet()) { - if (links.get(npt) == null) continue; + if (links.get(npt) == null) { + continue; + } for (Link link : links.get(npt)) { - if (link == null) continue; - if((int)link.getLatency().getValue() < 0 || (int)link.getLatency().getValue() > MAX_LINK_WEIGHT) + if (link == null) { + continue; + } + if ((int)link.getLatency().getValue() < 0 || + (int)link.getLatency().getValue() > MAX_LINK_WEIGHT) { linkCost.put(link, MAX_LINK_WEIGHT); - else + } 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.debug("Using Link Speed for Route Metrics"); + TopologyManager.statisticsService.collectStatistics(true); + log.debug("Using link speed for path metrics"); for (NodePortTuple npt : links.keySet()) { - if (links.get(npt) == null) continue; - rawLinkSpeed = TopologyManager.statisticsService.getLinkSpeed(npt); + if (links.get(npt) == null) { + continue; + } + long rawLinkSpeed = 0; + IOFSwitch s = TopologyManager.switchService.getSwitch(npt.getNodeId()); + if (s != null) { + OFPortDesc p = s.getPort(npt.getPortId()); + if (p != null) { + rawLinkSpeed = p.getCurrSpeed(); + } + } for (Link link : links.get(npt)) { - if (link == null) continue; + if (link == null) { + continue; + } - if((rawLinkSpeed / 10^6) / 8 > 1){ - linkSpeedMBps = (int)(rawLinkSpeed / 10^6) / 8; + if ((rawLinkSpeed / 10^6) / 8 > 1) { + int linkSpeedMBps = (int)(rawLinkSpeed / 10^6) / 8; linkCost.put(link, (1/linkSpeedMBps)*1000); + } else { + linkCost.put(link, MAX_LINK_WEIGHT); } - else + } + } + return linkCost; + + case UTILIZATION: + TopologyManager.statisticsService.collectStatistics(true); + log.debug("Using utilization for path metrics"); + for (NodePortTuple npt : links.keySet()) { + if (links.get(npt) == null) continue; + SwitchPortBandwidth spb = TopologyManager.statisticsService + .getBandwidthConsumption(npt.getNodeId(), npt.getPortId()); + long bpsTx = spb.getBitsPerSecondTx().getValue(); + for (Link link : links.get(npt)) { + if (link == null) { + continue; + } + + if ((bpsTx / 10^6) / 8 > 1) { + int cost = (int) (bpsTx / 10^6) / 8; + linkCost.put(link, ((1/cost)*1000)); + } else { linkCost.put(link, MAX_LINK_WEIGHT); + } } } return linkCost; default: - if(TopologyManager.collectStatistics == true){ - TopologyManager.statisticsService.collectStatistics(false); - TopologyManager.collectStatistics = false; - } log.debug("Invalid Selection: Using Default Hop Count with Tunnel Bias for Metrics"); for (NodePortTuple npt : portsTunnel) { if (links.get(npt) == null) continue; diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index ffeb928fda25e0826ee39b0ee0576026734901fe..77dcc9651f6fc08c3b7bd76af854be4b19fee9b5 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -62,16 +62,12 @@ import java.util.concurrent.TimeUnit; * through the topology. */ public class TopologyManager implements IFloodlightModule, ITopologyService, - ITopologyManagerBackend, ILinkDiscoveryListener, IOFMessageListener { - - protected static Logger log = LoggerFactory.getLogger(TopologyManager.class); +ITopologyManagerBackend, ILinkDiscoveryListener, IOFMessageListener { + private static Logger log = LoggerFactory.getLogger(TopologyManager.class); public static final String MODULE_NAME = "topology"; - protected static IStatisticsService statisticsService; - protected static volatile PATH_METRIC pathMetric = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; //default: compute paths on hop count - protected static boolean collectStatistics = false; - + /** * Maximum number of route entries stored in memory. */ @@ -107,12 +103,13 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, */ protected Set<NodePortTuple> tunnelPorts; - protected ILinkDiscoveryService linkDiscoveryService; - protected IThreadPoolService threadPoolService; - protected IFloodlightProviderService floodlightProviderService; - protected IOFSwitchService switchService; - protected IRestApiService restApiService; - protected IDebugCounterService debugCounterService; + protected static ILinkDiscoveryService linkDiscoveryService; + protected static IThreadPoolService threadPoolService; + protected static IFloodlightProviderService floodlightProviderService; + protected static IOFSwitchService switchService; + protected static IRestApiService restApiService; + protected static IDebugCounterService debugCounterService; + protected static IStatisticsService statisticsService; // Modules that listen to our updates protected ArrayList<ITopologyListener> topologyAware; @@ -177,8 +174,8 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, @Override public void run() { try { - if (ldUpdates.peek() != null) { - updateTopology(); + if (ldUpdates.peek() != null) { /* must check here, otherwise will run every interval */ + updateTopology("link-discovery-updates", false); } handleMiscellaneousPeriodicEvents(); } @@ -197,13 +194,17 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, return; } - public boolean updateTopology() { + public synchronized boolean updateTopology(String reason, boolean forced) { boolean newInstanceFlag; linksUpdated = false; dtLinksUpdated = false; tunnelPortsUpdated = false; - List<LDUpdate> appliedUpdates = applyUpdates(); - newInstanceFlag = createNewInstance("link-discovery-updates"); + List<LDUpdate> appliedUpdates = null; + if (this.ldUpdates.peek() != null) { + appliedUpdates = applyUpdates(); + } + log.info("Recomputing topology due to: {}", reason); + newInstanceFlag = createNewInstance(reason, forced); lastUpdateTime = new Date(); informListeners(appliedUpdates); return newInstanceFlag; @@ -621,22 +622,23 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, pathMetric = PATH_METRIC.LINK_SPEED; break; default: - log.error("Invalid routing metric {}. Using default {}", metric, pathMetric.getMetricName()); + log.error("Invalid routing metric {}. Using default {}", + metric, pathMetric.getMetricName()); break; } } log.info("Path metrics set to {}", pathMetric); - String maxroutes = configOptions.get("maxPathsToCompute") != null + String maxroutes = configOptions.get("maxPathsToCompute") != null ? configOptions.get("maxPathsToCompute").trim() : null; - if (maxroutes != null) { - try { - maxPathsToCompute = Integer.parseInt(maxroutes); - } catch (NumberFormatException e) { - log.error("Invalid 'maxPathsToCompute'. Using default {}", maxPathsToCompute); - } - } - log.info("Will compute a max of {} paths upon topology updates", maxPathsToCompute); + if (maxroutes != null) { + try { + maxPathsToCompute = Integer.parseInt(maxroutes); + } catch (NumberFormatException e) { + log.error("Invalid 'maxPathsToCompute'. Using default {}", maxPathsToCompute); + } + } + log.info("Will compute a max of {} paths upon topology updates", maxPathsToCompute); } @Override @@ -932,7 +934,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, } public boolean createNewInstance() { - return createNewInstance("internal"); + return createNewInstance("internal", false); } /** @@ -941,10 +943,12 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, * and tunnel ports. The method returns if a new instance of * topology was created or not. */ - protected boolean createNewInstance(String reason) { + protected boolean createNewInstance(String reason, boolean forced) { Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>(); - if (!linksUpdated) return false; + if (!linksUpdated && !forced) { + return false; + } Map<NodePortTuple, Set<Link>> openflowLinks; openflowLinks = @@ -1064,9 +1068,12 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, public void informListeners(List<LDUpdate> linkUpdates) { - - if (role != null && role != HARole.ACTIVE) + if (linkUpdates == null || linkUpdates.isEmpty()) { + return; + } + if (role != null && role != HARole.ACTIVE) { return; + } for(int i=0; i < topologyAware.size(); ++i) { ITopologyListener listener = topologyAware.get(i); @@ -1248,7 +1255,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, linksUpdated = true; dtLinksUpdated = true; tunnelPortsUpdated = true; - createNewInstance("startup"); + createNewInstance("startup", false); lastUpdateTime = new Date(); } @@ -1320,4 +1327,10 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, public TopologyInstance getCurrentTopologyInstance() { return getCurrentInstance(); } + + @Override + public boolean forceRecompute() { + /* cannot invoke scheduled executor, since the update might not occur */ + return updateTopology("forced-recomputation", true); + } } \ No newline at end of file