WIP: Refactoring the Intent REST API.
NOTE: All changes are Work-in-Progress, and they are in the process
of being actively updated and refactored.
* The new implementation is in onos/core/intent/runtime/web
The old implementation in the onos/core/datagrid/web is kept until
the refactoring is completed.
* The new REST API base path is /wm/onos/intent
* The initial set of (new) APIs is the following:
- /wm/onos/intent/high
GET all high-level intent
POST (create) a collection of high-level intents
DELETE all intents
- /wm/onos/intent/high/{intent-id}
GET a high-level intent object
DELETE a high-level intent object
- /wm/onos/intent/low
GET all low-level intents
- /wm/onos/intent/low/{intent-id}
GET a low-level intent object
- /wm/onos/intent/path/switch/{src-dpid}/shortest-path/{dst-dpid}
GET a Shortest Path between two Switch DPIDs
* The Application-Level Intent object is specified in class
onos/api/intent/ApplicationIntent.java
TODO (list incomplete):
- Return the appropriate REST codes and return values for each REST operation
- Add the appropriate Java APIs so each REST call would make a single
Java call.
- If necessary, rename API class ApplicationIntent to something more
appropriate, and use it in the Java API.
- Re-think/refactor the ApplicationIntent so it becomes a more solid base for
all (high-level) intents.
- The corresponding Java APIs for each REST call should be synchronous
(for some definition of the expected operation outcome).
- Implement intent/intents Java API delete operation that requires a
single call instead of two calls (delete and purge)
- Refactor the High and Low Level intents, such that they don't use
inheritance from a common base class.
- Cleanup the return Intent objects representation in JSON. E.g.,
the Dpid JSON should be just "dpid": <value> instead of
"dpid": {
"value": <value>
}
Change-Id: Ia994a2026f57a1f176c5321c1952325e3b986097
diff --git a/src/main/java/net/onrc/onos/api/intent/ApplicationIntent.java b/src/main/java/net/onrc/onos/api/intent/ApplicationIntent.java
new file mode 100644
index 0000000..113e6cd
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/intent/ApplicationIntent.java
@@ -0,0 +1,216 @@
+package net.onrc.onos.api.intent;
+
+/**
+ * An object used by the application to specify an intent.
+ */
+public class ApplicationIntent {
+ private String intentId; // The Intent ID
+ private String intentType; // The Intent type
+
+ // If true, don't update the path when topology changes
+ private boolean isStaticPath;
+
+ private String srcSwitchDpid; // Flow Path Source Switch DPID
+ private int srcSwitchPort; // Flow Path Source Switch Port
+ private String dstSwitchDpid; // Flow Path Destination Switch DPID
+ private int dstSwitchPort; // Flow Path Destination Switch Port
+ private double bandwidth; // Bandwidth for Constrained Shortest Path
+
+ // Matching Fields
+ private String matchSrcMac; // Matching source MAC address
+ private String matchDstMac; // Matching destination MAC address
+
+ /**
+ * Gets the Intent ID.
+ *
+ * @return the Intent ID.
+ */
+ public String intentId() {
+ return this.intentId;
+ }
+
+ /**
+ * Sets the Intent ID.
+ *
+ * @param intentId the Intent ID to set.
+ */
+ public void setIntentId(String intentId) {
+ this.intentId = intentId;
+ }
+
+ /**
+ * Gets the Intent type.
+ *
+ * Currently, the following strings are recognized:
+ * - "SHORTEST_PATH"
+ * - "CONSTRAINED_SHORTEST_PATH"
+ *
+ * @return the Intent type.
+ */
+ public String intentType() {
+ return this.intentType;
+ }
+
+ /**
+ * Sets the Intent type.
+ *
+ * Currently, the following strings are recognized:
+ * - "SHORTEST_PATH"
+ * - "CONSTRAINED_SHORTEST_PATH"
+ *
+ * @param intentType the Intent type to set.
+ */
+ public void setIntentType(String intentType) {
+ this.intentType = intentType;
+ }
+
+ /**
+ * Gets the "staticPath" flag for the intent.
+ *
+ * A path for an intent is defined as "static" if it shouldn't be updated
+ * when the topology changes.
+ *
+ * @return true if the intent path is static, otherwise false.
+ */
+ public boolean isStaticPath() {
+ return this.isStaticPath;
+ }
+
+ /**
+ * Sets the "staticPath" flag for the intent.
+ *
+ * A path for an intent is defined as "static" if it shouldn't be updated
+ * when the topology changes.
+ *
+ * @param staticPath true if the intent path is static, otherwise false.
+ */
+ public void setStaticPath(boolean staticPath) {
+ this.isStaticPath = staticPath;
+ }
+
+ /**
+ * Gets the Source Switch DPID.
+ *
+ * @return the Source Switch DPID.
+ */
+ public String srcSwitchDpid() {
+ return this.srcSwitchDpid;
+ }
+
+ /**
+ * Sets the Source Switch DPID.
+ *
+ * @param srcSwitchDpid the Source Switch DPID to set.
+ */
+ public void setSrcSwitchDpid(String srcSwitchDpid) {
+ this.srcSwitchDpid = srcSwitchDpid;
+ }
+
+ /**
+ * Gets the Source Switch Port.
+ *
+ * @return the Source Switch Port.
+ */
+ public int srcSwitchPort() {
+ return this.srcSwitchPort;
+ }
+
+ /**
+ * Sets the Source Switch Port.
+ *
+ * @param srcSwitchPort the Source Switch Port to set.
+ */
+ public void setSrcSwitchPort(int srcSwitchPort) {
+ this.srcSwitchPort = srcSwitchPort;
+ }
+
+ /**
+ * Gets the Destination Switch DPID.
+ *
+ * @return the Destination Switch DPID.
+ */
+ public String dstSwitchDpid() {
+ return this.dstSwitchDpid;
+ }
+
+ /**
+ * Sets the Destination Switch DPID.
+ *
+ * @param dstSwitchDpid the Destination Switch DPID to set.
+ */
+ public void setDstSwitchDpid(String dstSwitchDpid) {
+ this.dstSwitchDpid = dstSwitchDpid;
+ }
+
+ /**
+ * Gets the Destination Switch Port.
+ *
+ * @return the Destination Switch Port.
+ */
+ public int dstSwitchPort() {
+ return this.dstSwitchPort;
+ }
+
+ /**
+ * Sets the Destination Switch Port.
+ *
+ * @param dstSwitchPort the Destination Switch Port to set.
+ */
+ public void setDstSwitchPort(int dstSwitchPort) {
+ this.dstSwitchPort = dstSwitchPort;
+ }
+
+ /**
+ * Gets the bandwidth for Constrained Shortest Path.
+ *
+ * @return the bandwidth for Constrained Shortest Path.
+ */
+ public double bandwidth() {
+ return this.bandwidth;
+ }
+
+ /**
+ * Sets the bandwidth for Constrained Shortest Path.
+ *
+ * @param bandwidth the bandwidth for Constrained Shortest Path
+ */
+ public void setBandwidth(double bandwidth) {
+ this.bandwidth = bandwidth;
+ }
+
+ /**
+ * Gets the matching source MAC address.
+ *
+ * @return the matching source MAC address.
+ */
+ public String matchSrcMac() {
+ return this.matchSrcMac;
+ }
+
+ /**
+ * Sets the matching source MAC address.
+ *
+ * @param matchSrcMac the matching source MAC address to set.
+ */
+ public void setMatchSrcMac(String matchSrcMac) {
+ this.matchSrcMac = matchSrcMac;
+ }
+
+ /**
+ * Gets the matching destination MAC address.
+ *
+ * @return the matching destination MAC address.
+ */
+ public String matchDstMac() {
+ return this.matchDstMac;
+ }
+
+ /**
+ * Sets the matching destination MAC address.
+ *
+ * @param matchDstMac the matching destination MAC address to set.
+ */
+ public void setMatchDstMac(String matchDstMac) {
+ this.matchDstMac = matchDstMac;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
index a9ba287..1e6ae00 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
@@ -14,6 +14,8 @@
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+
import net.onrc.onos.core.datagrid.IDatagridService;
import net.onrc.onos.core.datagrid.IEventChannel;
import net.onrc.onos.core.datagrid.IEventChannelListener;
@@ -26,6 +28,7 @@
import net.onrc.onos.core.intent.PathIntent;
import net.onrc.onos.core.intent.PathIntentMap;
import net.onrc.onos.core.intent.ShortestPathIntent;
+import net.onrc.onos.core.intent.runtime.web.IntentWebRoutable;
import net.onrc.onos.core.registry.IControllerRegistryService;
import net.onrc.onos.core.topology.DeviceEvent;
import net.onrc.onos.core.topology.INetworkGraphListener;
@@ -82,6 +85,7 @@
private PathIntentMap pathIntents;
private IControllerRegistryService controllerRegistry;
private PersistIntent persistIntent;
+ private IRestApiService restApi;
private IEventChannel<Long, IntentOperationList> opEventChannel;
private final ReentrantLock lock = new ReentrantLock();
@@ -137,6 +141,7 @@
Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(2);
l.add(IDatagridService.class);
l.add(INetworkGraphService.class);
+ l.add(IRestApiService.class);
return l;
}
@@ -145,6 +150,7 @@
datagridService = context.getServiceImpl(IDatagridService.class);
networkGraphService = context.getServiceImpl(INetworkGraphService.class);
controllerRegistry = context.getServiceImpl(IControllerRegistryService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
}
@Override
@@ -156,6 +162,7 @@
datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
networkGraphService.registerNetworkGraphListener(this);
persistIntent = new PersistIntent(controllerRegistry);
+ restApi.addRestletRoutable(new IntentWebRoutable());
}
// ================================================================================
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighObjectResource.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighObjectResource.java
new file mode 100644
index 0000000..c4e2ac4
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighObjectResource.java
@@ -0,0 +1,89 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentMap;
+import net.onrc.onos.core.intent.IntentOperation;
+import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
+
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class to access a single high-level intent.
+ */
+public class IntentHighObjectResource extends ServerResource {
+ private static final Logger log = LoggerFactory.getLogger(IntentHighObjectResource.class);
+ // TODO need to assign proper application id.
+ private static final String APPLN_ID = "1";
+
+ /**
+ * Gets a single high-level intent.
+ *
+ * @return a Collection with the single high-level intent if found,
+ * otherwise null.
+ */
+ @Get("json")
+ public Collection<Intent> retrieve() throws IOException {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+ Collection<Intent> intents = null;
+
+ String intentId = (String) getRequestAttributes().get("intent-id");
+ if (intentId == null) {
+ return null; // Missing Intent ID
+ }
+
+ //
+ // Get a single high-level Intent: use the Intent ID to find it
+ //
+ IntentMap intentMap = pathRuntime.getHighLevelIntents();
+ String applnIntentId = APPLN_ID + ":" + intentId;
+ Intent intent = intentMap.getIntent(applnIntentId);
+ if (intent != null) {
+ intents = new LinkedList<>();
+ intents.add(intent);
+ }
+
+ return intents;
+ }
+
+ /**
+ * Deletes a single high-level intent.
+ *
+ * @return the status of the operation (TBD).
+ */
+ @Delete("json")
+ public String store() {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+
+ String intentId = (String) getRequestAttributes().get("intent-id");
+ if (intentId == null) {
+ return null; // Missing Intent ID
+ }
+
+ //
+ // Remove a single high-level Intent: use the Intent ID to find it
+ //
+ //
+ // TODO: The implementation below is broken - waiting for the Java API
+ // TODO: The deletion should use synchronous Java API?
+ IntentMap intentMap = pathRuntime.getHighLevelIntents();
+ String applnIntentId = APPLN_ID + ":" + intentId;
+ Intent intent = intentMap.getIntent(applnIntentId);
+ if (intent != null) {
+ IntentOperationList operations = new IntentOperationList();
+ operations.add(IntentOperation.Operator.REMOVE, intent);
+ pathRuntime.executeIntentOperations(operations);
+ }
+ return ""; // TODO no reply yet from the purge intents call
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighResource.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighResource.java
new file mode 100644
index 0000000..ede564f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentHighResource.java
@@ -0,0 +1,149 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.api.intent.ApplicationIntent;
+import net.onrc.onos.core.intent.ConstrainedShortestPathIntent;
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentMap;
+import net.onrc.onos.core.intent.IntentOperation;
+import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.ShortestPathIntent;
+import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
+import net.onrc.onos.core.util.Dpid;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class to access the high-level intents.
+ */
+public class IntentHighResource extends ServerResource {
+ private static final Logger log = LoggerFactory.getLogger(IntentHighResource.class);
+ // TODO need to assign proper application id.
+ private static final String APPLN_ID = "1";
+
+ /**
+ * Gets all high-level intents.
+ *
+ * @return a collection with all high-level intents.
+ */
+ @Get("json")
+ public Collection<Intent> retrieve() throws IOException {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+
+ IntentMap intentMap = pathRuntime.getHighLevelIntents();
+ Collection<Intent> intents = intentMap.getAllIntents();
+
+ return intents;
+ }
+
+ /**
+ * Adds a collection of high-level intents.
+ *
+ * @return the status of the operation (TBD).
+ */
+ @Post("json")
+ public String store(String jsonIntent) throws IOException {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext()
+ .getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+ if (pathRuntime == null) {
+ log.warn("Failed to get path calc runtime");
+ return "";
+ }
+
+ String reply = "";
+ ObjectMapper mapper = new ObjectMapper();
+ ApplicationIntent[] addOperations = null;
+ try {
+ addOperations = mapper.readValue(jsonIntent, ApplicationIntent[].class);
+ } catch (JsonParseException ex) {
+ log.error("JsonParseException occurred", ex);
+ } catch (JsonMappingException ex) {
+ log.error("JsonMappingException occurred", ex);
+ } catch (IOException ex) {
+ log.error("IOException occurred", ex);
+ }
+ if (addOperations == null) {
+ return "";
+ }
+
+ //
+ // Process all intents one-by-one
+ //
+ // TODO: The Intent Type should be enum instead of a string,
+ // and we should use a switch statement below to process the
+ // different type of intents.
+ //
+ IntentOperationList intentOperations = new IntentOperationList();
+ for (ApplicationIntent oper : addOperations) {
+ String applnIntentId = APPLN_ID + ":" + oper.intentId();
+
+ IntentOperation.Operator operator = IntentOperation.Operator.ADD;
+ Dpid srcSwitchDpid = new Dpid(oper.srcSwitchDpid());
+ Dpid dstSwitchDpid = new Dpid(oper.dstSwitchDpid());
+
+ if (oper.intentType().equals("SHORTEST_PATH")) {
+ //
+ // Process Shortest-Path Intent
+ //
+ ShortestPathIntent spi =
+ new ShortestPathIntent(applnIntentId,
+ srcSwitchDpid.value(),
+ oper.srcSwitchPort(),
+ MACAddress.valueOf(oper.matchSrcMac()).toLong(),
+ dstSwitchDpid.value(),
+ oper.dstSwitchPort(),
+ MACAddress.valueOf(oper.matchDstMac()).toLong());
+ spi.setPathFrozen(oper.isStaticPath());
+ intentOperations.add(operator, spi);
+ } else {
+ //
+ // Process Constrained Shortest-Path Intent
+ //
+ ConstrainedShortestPathIntent cspi =
+ new ConstrainedShortestPathIntent(applnIntentId,
+ srcSwitchDpid.value(),
+ oper.srcSwitchPort(),
+ MACAddress.valueOf(oper.matchSrcMac()).toLong(),
+ dstSwitchDpid.value(),
+ oper.dstSwitchPort(),
+ MACAddress.valueOf(oper.matchDstMac()).toLong(),
+ oper.bandwidth());
+ cspi.setPathFrozen(oper.isStaticPath());
+ intentOperations.add(operator, cspi);
+ }
+ }
+ // Apply the Intent Operations
+ pathRuntime.executeIntentOperations(intentOperations);
+
+ return reply;
+ }
+
+ /**
+ * Deletes all high-level intents.
+ *
+ * @return the status of the operation (TBD).
+ */
+ @Delete("json")
+ public String store() {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+
+ // Delete all intents
+ // TODO: The implementation below is broken - waiting for the Java API
+ // TODO: The deletion should use synchronous Java API?
+ pathRuntime.purgeIntents();
+ return ""; // TODO no reply yet from the purge intents call
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowObjectResource.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowObjectResource.java
new file mode 100644
index 0000000..4f5cc8a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowObjectResource.java
@@ -0,0 +1,54 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentMap;
+import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class to access a single low-level intent.
+ */
+public class IntentLowObjectResource extends ServerResource {
+ private static final Logger log = LoggerFactory.getLogger(IntentLowObjectResource.class);
+ // TODO need to assign proper application id.
+ private static final String APPLN_ID = "1";
+
+ /**
+ * Gets a single low-level intent.
+ *
+ * @return a Collection with the single low-level intent if found,
+ * otherwise null.
+ */
+ @Get("json")
+ public Collection<Intent> retrieve() throws IOException {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+ Collection<Intent> intents = null;
+
+ String intentId = (String) getRequestAttributes().get("intent-id");
+ if (intentId == null) {
+ return null; // Missing Intent ID
+ }
+
+ //
+ // Get a single low-level Intent: use the Intent ID to find it
+ //
+ IntentMap intentMap = pathRuntime.getPathIntents();
+ String applnIntentId = APPLN_ID + ":" + intentId;
+ Intent intent = intentMap.getIntent(applnIntentId);
+ if (intent != null) {
+ intents = new LinkedList<>();
+ intents.add(intent);
+ }
+
+ return intents;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowResource.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowResource.java
new file mode 100644
index 0000000..bf0b527
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentLowResource.java
@@ -0,0 +1,39 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentMap;
+import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class to access the low-level intents.
+ */
+public class IntentLowResource extends ServerResource {
+ private static final Logger log = LoggerFactory.getLogger(IntentLowResource.class);
+
+ /**
+ * Gets all low-level intents.
+ *
+ * @return a collection of all low-leve intents.
+ */
+ @Get("json")
+ public Collection<Intent> retrieve() throws IOException {
+ IPathCalcRuntimeService pathRuntime = (IPathCalcRuntimeService) getContext().
+ getAttributes().get(IPathCalcRuntimeService.class.getCanonicalName());
+
+ //
+ // Get all low-level intents
+ //
+ IntentMap intentMap = pathRuntime.getPathIntents();
+ Collection<Intent> intents = intentMap.getAllIntents();
+
+ return intents;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentWebRoutable.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentWebRoutable.java
new file mode 100644
index 0000000..4c32c47
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/IntentWebRoutable.java
@@ -0,0 +1,49 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * REST API implementation for the Intents.
+ */
+public class IntentWebRoutable implements RestletRoutable {
+ /**
+ * Creates the Restlet router and bind to the proper resources.
+ */
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ // GET all high-level intents
+ // POST (create) a collection of high-level intents
+ // DELETE all intents (TODO: delete a collection of high-level intents)
+ router.attach("/high", IntentHighResource.class);
+
+ // GET a high-level intent object
+ // PUT (update) a high-level intent object (entire object) (LATER?)
+ // DELETE a high-level intent object
+ router.attach("/high/{intent-id}", IntentHighObjectResource.class);
+
+ // GET all low-level intents
+ router.attach("/low", IntentLowResource.class);
+
+ // GET a low-level intent object
+ router.attach("/low/{intent-id}", IntentLowObjectResource.class);
+
+ // GET a Shortest Path between two Switch DPIDs
+ router.attach("/path/switch/{src-dpid}/shortest-path/{dst-dpid}",
+ ShortestPathResource.class);
+
+ return router;
+ }
+
+ /**
+ * Sets the base path for the Intents.
+ */
+ @Override
+ public String basePath() {
+ return "/wm/onos/intent";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/web/ShortestPathResource.java b/src/main/java/net/onrc/onos/core/intent/runtime/web/ShortestPathResource.java
new file mode 100644
index 0000000..6338d52
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/web/ShortestPathResource.java
@@ -0,0 +1,96 @@
+package net.onrc.onos.core.intent.runtime.web;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.onrc.onos.core.intent.ConstrainedBFSTree;
+import net.onrc.onos.core.intent.Path;
+import net.onrc.onos.core.topology.INetworkGraphService;
+import net.onrc.onos.core.topology.Link;
+import net.onrc.onos.core.topology.LinkEvent;
+import net.onrc.onos.core.topology.NetworkGraph;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.topology.serializers.LinkSerializer;
+import net.onrc.onos.core.util.Dpid;
+
+import org.codehaus.jackson.Version;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.module.SimpleModule;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class to access Shortest-Path information between switches.
+ */
+public class ShortestPathResource extends ServerResource {
+
+ private static final Logger log = LoggerFactory.getLogger(ShortestPathResource.class);
+
+ /**
+ * Gets the Shortest-Path infomration between switches.
+ *
+ * @return a JSON string with the Shortest-Path information between
+ * switches. The Shortest-Path information is an ordered collection of
+ * Links.
+ */
+ @Get("json")
+ public String retrieve() {
+ INetworkGraphService networkGraphService =
+ (INetworkGraphService) getContext().getAttributes().
+ get(INetworkGraphService.class.getCanonicalName());
+
+ NetworkGraph graph = networkGraphService.getNetworkGraph();
+
+ //
+ // Prepare the JSON serializer
+ //
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule("module", new Version(1, 0, 0, null));
+ module.addSerializer(new LinkSerializer());
+ mapper.registerModule(module);
+
+ //
+ // Fetch the attributes
+ //
+ String srcDpidStr = (String) getRequestAttributes().get("src-dpid");
+ String dstDpidStr = (String) getRequestAttributes().get("dst-dpid");
+ Dpid srcDpid = new Dpid(srcDpidStr);
+ Dpid dstDpid = new Dpid(dstDpidStr);
+ log.debug("Getting Shortest Path {}--{}", srcDpidStr, dstDpidStr);
+
+ //
+ // Do the Shortest Path computation and return the result: a list of
+ // links.
+ //
+ graph.acquireReadLock();
+ try {
+ Switch srcSwitch = graph.getSwitch(srcDpid.value());
+ Switch dstSwitch = graph.getSwitch(dstDpid.value());
+ if ((srcSwitch == null) || (dstSwitch == null)) {
+ return "";
+ }
+ ConstrainedBFSTree bfsTree = new ConstrainedBFSTree(srcSwitch);
+ Path path = bfsTree.getPath(dstSwitch);
+ List<Link> links = new LinkedList<>();
+ for (LinkEvent linkEvent : path) {
+ Link link = graph.getLink(linkEvent.getSrc().getDpid(),
+ linkEvent.getSrc().getNumber(),
+ linkEvent.getDst().getDpid(),
+ linkEvent.getDst().getNumber());
+ if (link == null) {
+ return "";
+ }
+ links.add(link);
+ }
+ return mapper.writeValueAsString(links);
+ } catch (IOException e) {
+ log.error("Error writing Shortest Path to JSON", e);
+ return "";
+ } finally {
+ graph.releaseReadLock();
+ }
+ }
+}