Support for FIQL queries on REST retreive operations
[controller.git] / opendaylight / northbound / flowprogrammer / src / main / java / org / opendaylight / controller / flowprogrammer / northbound / FlowProgrammerNorthbound.java
index 1e88c4430548f6de805ee67c4441f49e6db89f3c..42bd59ea4577ff5c6d40b3ad4e8efaa976c900f5 100644 (file)
@@ -19,10 +19,12 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.ContextResolver;
 
 import org.codehaus.enunciate.jaxrs.ResponseCode;
 import org.codehaus.enunciate.jaxrs.StatusCodes;
@@ -34,10 +36,10 @@ import org.opendaylight.controller.northbound.commons.RestMessages;
 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
 import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException;
 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
-import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
+import org.opendaylight.controller.northbound.commons.query.QueryContext;
 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
 import org.opendaylight.controller.sal.authorization.Privilege;
 import org.opendaylight.controller.sal.core.Node;
@@ -63,6 +65,14 @@ public class FlowProgrammerNorthbound {
 
     private String username;
 
+    private QueryContext queryContext;
+    @Context
+    public void setQueryContext(ContextResolver<QueryContext> queryCtxResolver) {
+      if (queryCtxResolver != null) {
+        queryContext = queryCtxResolver.getContext(QueryContext.class);
+      }
+    }
+
     @Context
     public void setSecurityContext(SecurityContext context) {
         if (context != null && context.getUserPrincipal() != null) {
@@ -145,10 +155,10 @@ public class FlowProgrammerNorthbound {
      *
      * Example:
      *
-     * RequestURL:
+     * Request URL:
      * http://localhost:8080/controller/nb/v2/flowprogrammer/default
      *
-     * Response in XML:
+     * Response body in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
      * &lt;list&gt;
      *     &#x20;&#x20;&#x20;&lt;flowConfig&gt;
@@ -166,10 +176,26 @@ public class FlowProgrammerNorthbound {
      *     &#x20;&#x20;&#x20;&lt;/flowConfig&gt;
      * &lt;/list&gt;
      *
-     * Response in JSON:
-     * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
-     * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
-     *
+     * Response body in JSON:
+     * {
+     *   "flowConfig": [
+     *      {
+     *         "installInHw": "true",
+     *         "name": "flow1",
+     *         "node": {
+     *            "type": "OF",
+     *            "id": "00:00:00:00:00:00:00:01"
+     *         },
+     *         "ingressPort": "1",
+     *         "priority": "500",
+     *         "etherType": "0x800",
+     *         "nwSrc":"9.9.1.1",
+     *         "actions": [
+     *           "OUTPUT=2"
+     *         ]
+     *      }
+     *    ]
+     * }
      * </pre>
      */
     @Path("/{containerName}")
@@ -179,15 +205,21 @@ public class FlowProgrammerNorthbound {
     @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
         @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
         @ResponseCode(code = 404, condition = "The containerName is not found"),
-        @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
-    public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName) {
+        @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable"),
+        @ResponseCode(code = 400, condition = "Incorrect query syntex")})
+    public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName,
+                   @QueryParam("_q") String queryString) {
         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
             throw new UnauthorizedException("User is not authorized to perform this operation on container "
                     + containerName);
         }
 
-        List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName, null);
-        return new FlowConfigs(flowConfigs);
+        FlowConfigs result = new FlowConfigs(getStaticFlowsInternal(containerName, null));
+        if (queryString != null) {
+            queryContext.createQuery(queryString, FlowConfigs.class)
+                .filter(result, FlowConfig.class);
+        }
+        return result;
     }
 
     /**
@@ -205,10 +237,10 @@ public class FlowProgrammerNorthbound {
      *
      * Example:
      *
-     * RequestURL:
+     * Request URL:
      * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
      *
-     * Response in XML:
+     * Response body in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
      * &lt;list&gt;
      *     &#x20;&#x20;&#x20;&lt;flowConfig&gt;
@@ -226,10 +258,26 @@ public class FlowProgrammerNorthbound {
      *     &#x20;&#x20;&#x20;&lt;/flowConfig&gt;
      * &lt;/list&gt;
      *
-     * Response in JSON:
-     * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
-     * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
-     *
+    * Response body in JSON:
+     * {
+     *   "flowConfig": [
+     *      {
+     *         "installInHw": "true",
+     *         "name": "flow1",
+     *         "node": {
+     *            "type": "OF",
+     *            "id": "00:00:00:00:00:00:00:01"
+     *         },
+     *         "ingressPort": "1",
+     *         "priority": "500",
+     *         "etherType": "0x800",
+     *         "nwSrc":"9.9.1.1",
+     *         "actions": [
+     *           "OUTPUT=2"
+     *         ]
+     *       }
+     *    ]
+     * }
      * </pre>
      */
     @Path("/{containerName}/node/{nodeType}/{nodeId}")
@@ -241,7 +289,8 @@ public class FlowProgrammerNorthbound {
         @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
         @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
     public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName,
-            @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId) {
+            @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
+            @QueryParam("_q") String queryString) {
         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
             throw new UnauthorizedException("User is not authorized to perform this operation on container "
                     + containerName);
@@ -250,8 +299,12 @@ public class FlowProgrammerNorthbound {
         if (node == null) {
             throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
         }
-        List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
-        return new FlowConfigs(flows);
+        FlowConfigs flows = new FlowConfigs(getStaticFlowsInternal(containerName, node));
+        if (queryString != null) {
+            queryContext.createQuery(queryString, FlowConfigs.class)
+                .filter(flows, FlowConfig.class);
+        }
+        return flows;
     }
 
     /**
@@ -272,10 +325,10 @@ public class FlowProgrammerNorthbound {
      *
      * Example:
      *
-     * RequestURL:
+     * Request URL:
      * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
-     * Response in XML:
+     * Response body in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
      * &lt;flowConfig&gt;
      *     &#x20;&#x20;&#x20;&lt;installInHw&gt;true&lt;/installInHw&gt;
@@ -291,9 +344,22 @@ public class FlowProgrammerNorthbound {
      *     &#x20;&#x20;&#x20;&lt;actions&gt;OUTPUT=2&lt;/actions&gt;
      * &lt;/flowConfig&gt;
      *
-     * Response in JSON:
-     * {"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
-     * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
+    * Response body in JSON:
+     * {
+     *    "installInHw":"true",
+     *    "name":"flow1",
+     *    "node":{
+     *       "id":"00:00:00:00:00:00:00:01",
+     *       "type":"OF"
+     *    },
+     *    "ingressPort":"1",
+     *    "priority":"500",
+     *    "etherType":"0x800",
+     *    "nwSrc":"9.9.1.1",
+     *    "actions":[
+     *       "OUTPUT=2"
+     *    ]
+     * }
      *
      * </pre>
      */
@@ -328,8 +394,7 @@ public class FlowProgrammerNorthbound {
     }
 
     /**
-     * Add a flow configuration. If a flow by the given name already exists,
-     * this method will respond with a non-successful status response.
+     * Add or Modify a flow configuration. If the flow exists already, it will replace the current flow.
      *
      * @param containerName
      *            Name of the Container (Eg. 'default')
@@ -347,10 +412,10 @@ public class FlowProgrammerNorthbound {
      *
      * Example:
      *
-     * RequestURL:
+     * Request URL:
      * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
-     * Request in XML:
+     * Request body in XML:
      * &lt;flowConfig&gt;
      *         &#x20;&#x20;&#x20;&lt;installInHw&gt;true&lt;/installInHw&gt;
      *         &#x20;&#x20;&#x20;&lt;name&gt;flow1&lt;/name&gt;
@@ -365,10 +430,22 @@ public class FlowProgrammerNorthbound {
      *         &#x20;&#x20;&#x20;&lt;actions&gt;OUTPUT=2&lt;/actions&gt;
      * &lt;/flowConfig&gt;
      *
-     * Request in JSON:
-     * {"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
-     * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
-     *
+     * Request body in JSON:
+      * {
+     *    "installInHw":"true",
+     *    "name":"flow1",
+     *    "node":{
+     *       "id":"00:00:00:00:00:00:00:01",
+     *       "type":"OF"
+     *    },
+     *    "ingressPort":"1",
+     *    "priority":"500",
+     *    "etherType":"0x800",
+     *    "nwSrc":"9.9.1.1",
+     *    "actions":[
+     *       "OUTPUT=2"
+     *    ]
+     * }
      * </pre>
      */
 
@@ -376,6 +453,7 @@ public class FlowProgrammerNorthbound {
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
+        @ResponseCode(code = 200, condition = "Static Flow modified successfully"),
         @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
         @ResponseCode(code = 400, condition = "Failed to create Static Flow entry due to invalid flow configuration"),
         @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@@ -384,7 +462,7 @@ public class FlowProgrammerNorthbound {
         @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name or configuration"),
         @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
         @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
-    public Response addFlow(@PathParam(value = "containerName") String containerName,
+    public Response addOrModifyFlow(@PathParam(value = "containerName") String containerName,
             @PathParam(value = "name") String name, @PathParam("nodeType") String nodeType,
             @PathParam(value = "nodeId") String nodeId, @TypeHint(FlowConfig.class) FlowConfig flowConfig) {
 
@@ -398,7 +476,8 @@ public class FlowProgrammerNorthbound {
                     .build();
         }
         handleResourceCongruence(name, flowConfig.getName());
-        handleResourceCongruence(nodeId, flowConfig.getNode().getNodeIDString());
+        handleResourceCongruence(nodeType, flowConfig.getNode().getType());
+        handleResourceCongruence(nodeId, flowConfig.getNode().getID() == null ? null : flowConfig.getNode().getNodeIDString());
         handleDefaultDisabled(containerName);
 
         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
@@ -408,18 +487,24 @@ public class FlowProgrammerNorthbound {
         }
 
         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
+        Status status;
 
         FlowConfig staticFlow = frm.getStaticFlow(name, node);
-        if (staticFlow != null) {
-            throw new ResourceConflictException(name + " already exists." + RestMessages.RESOURCECONFLICT.toString());
-        }
-
-        Status status = frm.addStaticFlow(flowConfig);
 
-        if (status.isSuccess()) {
-            NorthboundUtils.auditlog("Flow Entry", username, "added",
-                    name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
-            return Response.status(Response.Status.CREATED).entity("Success").build();
+        if (staticFlow == null) {
+          status = frm.addStaticFlow(flowConfig);
+          if(status.isSuccess()){
+              NorthboundUtils.auditlog("Flow Entry", username, "added",
+                      name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
+              return Response.status(Response.Status.CREATED).entity("Success").build();
+          }
+        } else {
+          status = frm.modifyStaticFlow(flowConfig);
+          if(status.isSuccess()){
+              NorthboundUtils.auditlog("Flow Entry", username, "updated",
+                      name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
+              return NorthboundUtils.getResponse(status);
+          }
         }
         return NorthboundUtils.getResponse(status);
     }
@@ -449,7 +534,6 @@ public class FlowProgrammerNorthbound {
 
     @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
     @DELETE
-    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
         @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
         @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),