[ONOS-6760][ONOS-6761] RESTCONF changes and fixes
* bug fixes for POST and PUT
* added support for PATCH
* added support for RPC
Change-Id: Ib03dd1e92d4e7231851340063eb23e4b3ad582cf
diff --git a/protocols/restconf/server/rpp/BUCK b/protocols/restconf/server/rpp/BUCK
index 6531f88..7eaf628 100644
--- a/protocols/restconf/server/rpp/BUCK
+++ b/protocols/restconf/server/rpp/BUCK
@@ -3,6 +3,7 @@
'//lib:jersey-client',
'//lib:jersey-server',
'//lib:javax.ws.rs-api',
+ '//lib:servlet-api',
'//utils/rest:onlab-rest',
'//apps/restconf/api:onos-apps-restconf-api',
]
diff --git a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
index 4bd2168..1037567 100644
--- a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
+++ b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
@@ -22,9 +22,11 @@
import org.onosproject.rest.AbstractWebResource;
import org.onosproject.restconf.api.Patch;
import org.onosproject.restconf.api.RestconfException;
+import org.onosproject.restconf.api.RestconfRpcOutput;
import org.onosproject.restconf.api.RestconfService;
import org.slf4j.Logger;
+import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -39,9 +41,13 @@
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.OK;
import static org.slf4j.LoggerFactory.getLogger;
@@ -62,6 +68,8 @@
@Context
UriInfo uriInfo;
+ private static final String NOT_EXIST = "Requested data resource does not exist";
+
private final RestconfService service = get(RestconfService.class);
private final Logger log = getLogger(getClass());
@@ -75,14 +83,19 @@
* @return HTTP response
*/
@GET
+ @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handleGetRequest(@PathParam("identifier") String uriString) {
-
log.debug("handleGetRequest: {}", uriString);
+ URI uri = uriInfo.getRequestUri();
+
try {
- ObjectNode node = service.runGetOperationOnDataResource(uriString);
+ ObjectNode node = service.runGetOperationOnDataResource(uri);
+ if (node == null) {
+ return Response.status(NOT_FOUND).entity(NOT_EXIST).build();
+ }
return ok(node).build();
} catch (RestconfException e) {
log.error("ERROR: handleGetRequest: {}", e.getMessage());
@@ -102,15 +115,18 @@
* calls ChunkedOutput.close() to disconnect the session and terminates itself.
*
* @param streamId Event stream ID
+ * @param request RESTCONF client information from which the client IP
+ * address is retrieved
* @return A string data stream over HTTP keep-alive session
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("streams/{streamId}")
- public ChunkedOutput<String> handleNotificationRegistration(@PathParam("streamId") String streamId) {
+ public ChunkedOutput<String> handleNotificationRegistration(@PathParam("streamId") String streamId,
+ @Context HttpServletRequest request) {
final ChunkedOutput<String> output = new ChunkedOutput<>(String.class);
try {
- service.subscribeEventStream(streamId, output);
+ service.subscribeEventStream(streamId, request.getRemoteAddr(), output);
} catch (RestconfException e) {
log.error("ERROR: handleNotificationRegistration: {}", e.getMessage());
log.debug("Exception in handleNotificationRegistration:", e);
@@ -125,12 +141,31 @@
}
/**
+ * Handles a RESTCONF POST operation against the entire data store. If the
+ * operation is successful, HTTP status code "201 Created" is returned
+ * and there is no response message-body. If the data resource already
+ * exists, then the HTTP status code "409 Conflict" is returned.
+ *
+ * @param stream Input JSON object
+ * @return HTTP response
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("data")
+ public Response handlePostDatastore(InputStream stream) {
+
+ log.debug("handlePostDatastore");
+ return handlePostRequest(null, stream);
+ }
+
+ /**
* Handles a RESTCONF POST operation against a data resource. If the
* operation is successful, HTTP status code "201 Created" is returned
* and there is no response message-body. If the data resource already
* exists, then the HTTP status code "409 Conflict" is returned.
*
- * @param uriString URI of the data resource
+ * @param uriString URI of the parent data resource
* @param stream Input JSON object
* @return HTTP response
*/
@@ -140,13 +175,14 @@
@Path("data/{identifier : .+}")
public Response handlePostRequest(@PathParam("identifier") String uriString,
InputStream stream) {
-
log.debug("handlePostRequest: {}", uriString);
+ URI uri = uriInfo.getRequestUri();
+
try {
ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
- service.runPostOperationOnDataResource(uriString, rootNode);
+ service.runPostOperationOnDataResource(uri, rootNode);
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePostRequest ", e);
@@ -162,6 +198,50 @@
}
/**
+ * Handles a RESTCONF RPC request. This function executes the RPC in
+ * the target application's context and returns the results as a Future.
+ *
+ * @param rpcName Name of the RPC
+ * @param rpcInput Input parameters
+ * @param request RESTCONF client information from which the client IP
+ * address is retrieved
+ * @return RPC output
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("operations/{rpc : .+}")
+ public Response handleRpcRequest(@PathParam("rpc") String rpcName,
+ InputStream rpcInput,
+ @Context HttpServletRequest request) {
+ URI uri = uriInfo.getRequestUri();
+ try {
+ ObjectNode inputNode = (ObjectNode) mapper().readTree(rpcInput);
+ CompletableFuture<RestconfRpcOutput> rpcFuture = service.runRpc(uri,
+ inputNode,
+ request.getRemoteAddr());
+ RestconfRpcOutput restconfRpcOutput;
+ restconfRpcOutput = rpcFuture.get();
+ if (restconfRpcOutput.status() != OK) {
+ return Response.status(restconfRpcOutput.status())
+ .entity(restconfRpcOutput.reason()).build();
+ }
+ ObjectNode node = restconfRpcOutput.output();
+ return ok(node).build();
+ } catch (JsonProcessingException e) {
+ log.error("ERROR: handleRpcRequest", e);
+ return Response.status(BAD_REQUEST).build();
+ } catch (RestconfException e) {
+ log.error("ERROR: handleRpcRequest: {}", e.getMessage());
+ log.debug("Exception in handleRpcRequest:", e);
+ return e.getResponse();
+ } catch (Exception e) {
+ log.error("ERROR: handleRpcRequest ", e);
+ return Response.status(INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
* Handles a RESTCONF PUT operation against a data resource. If a new
* resource is successfully created, then the HTTP status code "201 Created"
* is returned. If an existing resource is modified, then the HTTP
@@ -181,13 +261,14 @@
@Path("data/{identifier : .+}")
public Response handlePutRequest(@PathParam("identifier") String uriString,
InputStream stream) {
-
log.debug("handlePutRequest: {}", uriString);
+ URI uri = uriInfo.getRequestUri();
+
try {
ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
- service.runPutOperationOnDataResource(uriString, rootNode);
+ service.runPutOperationOnDataResource(uri, rootNode);
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePutRequest ", e);
@@ -213,14 +294,16 @@
* @return HTTP response
*/
@DELETE
+ @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handleDeleteRequest(@PathParam("identifier") String uriString) {
-
log.debug("handleDeleteRequest: {}", uriString);
+ URI uri = uriInfo.getRequestUri();
+
try {
- service.runDeleteOperationOnDataResource(uriString);
+ service.runDeleteOperationOnDataResource(uri);
return Response.ok().build();
} catch (RestconfException e) {
log.error("ERROR: handleDeleteRequest: {}", e.getMessage());
@@ -235,7 +318,7 @@
* there is a message-body, and "204 No Content" is returned if no
* response message-body is sent.
*
- * @param uriString URI of the data resource
+ * @param uriString URI of the parent data resource
* @param stream Input JSON object
* @return HTTP response
*/
@@ -248,10 +331,12 @@
log.debug("handlePatchRequest: {}", uriString);
+ URI uri = uriInfo.getRequestUri();
+
try {
ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
- service.runPatchOperationOnDataResource(uriString, rootNode);
+ service.runPatchOperationOnDataResource(uri, rootNode);
return Response.ok().build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePatchRequest ", e);
@@ -266,4 +351,22 @@
}
}
+
+ /**
+ * Handles a RESTCONF PATCH operation against the entire data store.
+ * If the PATCH request succeeds, a "200 OK" status-line is returned if
+ * there is a message-body, and "204 No Content" is returned if no
+ * response message-body is sent.
+ *
+ * @param stream Input JSON object
+ * @return HTTP response
+ */
+ @Patch
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("data")
+ public Response handlePatchDatastore(InputStream stream) {
+ log.debug("handlePatchDatastore");
+ return handlePatchRequest(null, stream);
+ }
}