Skip to content
Snippets Groups Projects
Commit 43ff7797 authored by Ryan Izard's avatar Ryan Izard
Browse files

complete routing/path-finding REST API

parent 58b7d9e6
No related branches found
No related tags found
No related merge requests found
Showing
with 299 additions and 53 deletions
......@@ -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();
}
......@@ -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
/**
* 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
/**
* 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
......@@ -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
......@@ -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;
}
......
......@@ -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
package net.floodlightcontroller.topology;
import net.floodlightcontroller.routing.IRoutingService;
public interface ITopologyPathFinding extends IRoutingService {
}
......@@ -62,16 +62,15 @@ 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.
*/
......@@ -177,8 +176,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 +196,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 +624,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 +936,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService,
}
public boolean createNewInstance() {
return createNewInstance("internal");
return createNewInstance("internal", false);
}
/**
......@@ -941,10 +945,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 +1070,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 +1257,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService,
linksUpdated = true;
dtLinksUpdated = true;
tunnelPortsUpdated = true;
createNewInstance("startup");
createNewInstance("startup", false);
lastUpdateTime = new Date();
}
......@@ -1320,4 +1329,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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment