Added initial implementation of the Titan + Gremlin based shortest-path
computation. It replaces the existing Floodlight backend, and
can be used with the existing REST-based API.
Added a test Python-script which is just a wrapper for
the "curl" call.
diff --git a/shortest_path.py b/shortest_path.py
new file mode 100755
index 0000000..5bea182
--- /dev/null
+++ b/shortest_path.py
@@ -0,0 +1,71 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+from flask import Flask, json, Response, render_template, make_response, request
+
+#
+# curl http://127.0.0.1:8080/wm/topology/route/00:00:00:00:00:00:0a:01/1/00:00:00:00:00:00:0a:04/1/json
+#
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+pp = pprint.PrettyPrinter(indent=4)
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+ print '%s' % (txt)
+
+def debug(txt):
+ if DEBUG:
+ print '%s' % (txt)
+
+# @app.route("/wm/topology/route/<srcdpid>/<srcport>/<destdpid>/<destport>/json")
+def shortest_path(v1, p1, v2, p2):
+ try:
+ command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+ result = os.popen(command).read()
+ parsedResult = json.loads(result)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ debug("shortest_path %s" % command)
+ debug("parsed %s" % parsedResult)
+
+ for v in parsedResult:
+ dpid = v['switch'];
+ port = v['port'];
+ print "PathEntry: (%s, %s)" % (dpid, port)
+
+
+if __name__ == "__main__":
+ usage_msg = "Usage: %s <src-dpid> <src-port> <dest-dpid> <dest-port>" % (sys.argv[0])
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+ if len(sys.argv) < 5:
+ log_error(usage_msg)
+ exit(1)
+
+ # Do the work
+ shortest_path(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]);
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
new file mode 100644
index 0000000..be78494
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -0,0 +1,150 @@
+package net.floodlightcontroller.routing;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import net.floodlightcontroller.core.internal.SwitchStorageImpl;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.topology.NodePortTuple;
+
+import org.openflow.util.HexString;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+// import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Vertex;
+// import com.tinkerpop.gremlin.groovy.Gremlin;
+// import com.tinkerpop.gremlin.java.GremlinPipeline;
+// import com.tinkerpop.pipes.Pipe;
+// import com.tinkerpop.pipes.PipeFunction;
+// import com.tinkerpop.pipes.branch.LoopPipe;
+// import com.tinkerpop.pipes.branch.LoopPipe.LoopBundle;
+// import com.tinkerpop.pipes.filter.FilterPipe.Filter;
+// import com.tinkerpop.pipes.util.PipesFluentPipeline;
+
+import com.tinkerpop.blueprints.Element;
+
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
+
+public class TopoRouteService implements ITopoRouteService {
+
+ ThreadLocal<SwitchStorageImpl> store = new ThreadLocal<SwitchStorageImpl>() {
+ @Override
+ protected SwitchStorageImpl initialValue() {
+ SwitchStorageImpl swStore = new SwitchStorageImpl();
+ // NOTE: This is the file path from global properties
+ swStore.init("/tmp/cassandra.titan");
+ return swStore;
+ }
+ };
+
+ SwitchStorageImpl swStore = store.get();
+
+ @Override
+ public List<NodePortTuple> GetShortestpath(NodePortTuple src,
+ NodePortTuple dest) {
+ List<NodePortTuple> result_list = new ArrayList<NodePortTuple>();
+
+ TitanGraph titanGraph = swStore.graph;
+
+ String dpid_src = HexString.toHexString(src.getNodeId());
+ String dpid_dest = HexString.toHexString(dest.getNodeId());
+
+ //
+ // Implement the Shortest Path between two vertices by using
+ // the following Gremlin code:
+ // results = []; v_src.as('x').out.out.in.has("type", "switch").dedup().loop('x'){it.object.dpid != v_dest.dpid & it.loops < 10}.path().fill(results)
+ //
+
+ String gremlin = "v_src.as(\"x\").out.out.in.has(\"type\", \"switch\").dedup().loop(\"x\"){it.object.dpid != v_dest.dpid & it.loops < 10}.path().fill(results)";
+
+ // Get the source vertex
+ Iterator<Vertex> iter = titanGraph.getVertices("dpid", dpid_src).iterator();
+ if (! iter.hasNext())
+ return null; // Source vertex not found
+ Vertex v_src = iter.next();
+
+ // Get the destination vertex
+ iter = titanGraph.getVertices("dpid", dpid_dest).iterator();
+ if (! iter.hasNext())
+ return null; // Destination vertex not found
+ Vertex v_dest = iter.next();
+
+ //
+ // Implement the Gremlin script and run it
+ //
+ ScriptEngine engine = new GremlinGroovyScriptEngine();
+
+ ArrayList<ArrayList<Vertex>> results = new ArrayList<ArrayList<Vertex>>();
+ engine.getBindings(ScriptContext.ENGINE_SCOPE).put("g", titanGraph);
+ engine.getBindings(ScriptContext.ENGINE_SCOPE).put("v_src", v_src);
+ engine.getBindings(ScriptContext.ENGINE_SCOPE).put("v_dest", v_dest);
+ engine.getBindings(ScriptContext.ENGINE_SCOPE).put("results", results);
+
+ try {
+ engine.eval(gremlin);
+ } catch (ScriptException e) {
+ System.err.println("Caught ScriptException running Gremlin script: " + e.getMessage());
+ return null;
+ }
+
+ //
+ // Loop through the result and return the list
+ // of <dpid, port> tuples.
+ //
+ long nodeId = 0;
+ short portId = 0;
+ for (ArrayList<Vertex> lv : results) {
+ int idx = 0;
+ for (Vertex v: lv) {
+ String type = v.getProperty("type").toString();
+ System.out.println("type: " + type);
+ if (type.equals("port")) {
+ String number = v.getProperty("number").toString();
+ System.out.println("number: " + number);
+
+ Object obj = v.getProperty("number");
+ // String class_str = obj.getClass().toString();
+ if (obj instanceof Short) {
+ portId = (Short)obj;
+ } else if (obj instanceof Integer) {
+ Integer int_nodeId = (Integer)obj;
+ portId = int_nodeId.shortValue();
+ // int int_nodeId = (Integer)obj;
+ // portId = (short)int_nodeId.;
+ }
+ } else if (type.equals("switch")) {
+ String dpid = v.getProperty("dpid").toString();
+ nodeId = HexString.toLong(dpid);
+
+ System.out.println("dpid: " + dpid);
+ }
+ if (idx == 0) {
+ idx++;
+ continue;
+ }
+ int mod = (idx - 1) % 3;
+ if ((mod == 0) || (mod == 2)) {
+ result_list.add(new NodePortTuple(nodeId, portId));
+ }
+ idx++;
+ }
+ }
+ if (result_list.size() > 0)
+ return result_list;
+
+ return null;
+ }
+
+ @Override
+ public Boolean RouteExists(NodePortTuple src, NodePortTuple dest) {
+ List<NodePortTuple> route = GetShortestpath(src, dest);
+ if (route != null)
+ return true;
+ return false;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/topology/web/RouteResource.java b/src/main/java/net/floodlightcontroller/topology/web/RouteResource.java
index 70e406f..56c9bf3 100644
--- a/src/main/java/net/floodlightcontroller/topology/web/RouteResource.java
+++ b/src/main/java/net/floodlightcontroller/topology/web/RouteResource.java
@@ -4,7 +4,9 @@
import net.floodlightcontroller.routing.IRoutingService;
import net.floodlightcontroller.routing.Route;
+import net.floodlightcontroller.routing.TopoRouteService;
import net.floodlightcontroller.topology.NodePortTuple;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
import org.openflow.util.HexString;
import org.restlet.resource.Get;
@@ -18,9 +20,16 @@
@Get("json")
public List<NodePortTuple> retrieve() {
+ /*
IRoutingService routing =
(IRoutingService)getContext().getAttributes().
get(IRoutingService.class.getCanonicalName());
+ */
+ ITopoRouteService onos_routing = new TopoRouteService();
+ if (onos_routing == null) {
+ log.debug("ONOS Route Service not found");
+ return null;
+ }
String srcDpid = (String) getRequestAttributes().get("src-dpid");
String srcPort = (String) getRequestAttributes().get("src-port");
@@ -34,12 +43,19 @@
long longDstDpid = HexString.toLong(dstDpid);
short shortDstPort = Short.parseShort(dstPort);
+ /*
Route result = routing.getRoute(longSrcDpid, shortSrcPort, longDstDpid, shortDstPort);
if (result!=null) {
return routing.getRoute(longSrcDpid, shortSrcPort, longDstDpid, shortDstPort).getPath();
}
- else {
+ */
+ List<NodePortTuple> result =
+ onos_routing.GetShortestpath(new NodePortTuple(longSrcDpid, shortSrcPort),
+ new NodePortTuple(longDstDpid, shortDstPort));
+ if ((result != null) && (result.size() > 0)) {
+ return result;
+ } else {
log.debug("ERROR! no route found");
return null;
}