REST APIs to add/delete ports to/from a subnet.
[controller.git] / opendaylight / northbound / subnets / src / main / java / org / opendaylight / controller / subnets / northbound / SubnetsNorthboundJAXRS.java
index 1e5336257de13187790ea2fc1b2997fc32ccfc63..e3c2189bf91550bd7c8d35526ec0d75413a88a6a 100644 (file)
@@ -8,8 +8,11 @@
 package org.opendaylight.controller.subnets.northbound;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -17,8 +20,11 @@ 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.xml.bind.JAXBElement;
 
 import org.codehaus.enunciate.jaxrs.ResponseCode;
 import org.codehaus.enunciate.jaxrs.StatusCodes;
@@ -26,6 +32,9 @@ import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.opendaylight.controller.northbound.commons.RestMessages;
 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
+import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
+import org.opendaylight.controller.sal.authorization.Privilege;
 import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
@@ -35,29 +44,41 @@ import org.slf4j.LoggerFactory;
 
 @Path("/")
 public class SubnetsNorthboundJAXRS {
-    protected static final Logger logger = LoggerFactory
-            .getLogger(SubnetsNorthboundJAXRS.class);
+    protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthboundJAXRS.class);
+
+    private String username;
+
+    @Context
+    public void setSecurityContext(SecurityContext context) {
+        username = context.getUserPrincipal().getName();
+    }
+
+    protected String getUserName() {
+        return username;
+    }
 
     /**
      * List all the subnets in a given container
      *
-     * @param containerName container in which we want to query the subnets
+     * @param containerName
+     *            container in which we want to query the subnets
      *
      * @return a List of SubnetConfig
      */
     @Path("/{containerName}")
     @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    @StatusCodes( { @ResponseCode(code = 404, condition = "The containerName passed was not found") })
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({ @ResponseCode(code = 404, condition = "The containerName passed was not found") })
     @TypeHint(SubnetConfigs.class)
-    public SubnetConfigs listSubnets(
-            @PathParam("containerName") String containerName) {
+    public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
         ISwitchManager switchManager = null;
-        switchManager = (ISwitchManager) ServiceHelper.getInstance(
-                ISwitchManager.class, containerName, this);
+        switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
         if (switchManager == null) {
-            throw new ResourceNotFoundException(RestMessages.NOCONTAINER
-                    .toString());
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
         }
         return new SubnetConfigs(switchManager.getSubnetsConfigList());
     }
@@ -65,58 +86,67 @@ public class SubnetsNorthboundJAXRS {
     /**
      * List the configuration of a subnet in a given container
      *
-     * @param containerName container in which we want to query the
-     * subnet
-     * @param subnetName of the subnet being queried
+     * @param containerName
+     *            container in which we want to query the subnet
+     * @param subnetName
+     *            of the subnet being queried
      *
      * @return a SubnetConfig
      */
     @Path("/{containerName}/{subnetName}")
     @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    @StatusCodes( {
-            @ResponseCode(code = 404, condition = "The containerName passed was not found"),
-            @ResponseCode(code = 404, condition = "Subnet does not exist") })
-    @TypeHint(SubnetConfig.class)
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+        @ResponseCode(code = 404, condition = "The containerName passed was not found"),
+        @ResponseCode(code = 404, condition = "Subnet does not exist") })
+        @TypeHint(SubnetConfig.class)
     public SubnetConfig listSubnet(
-            @PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName) {
+        @PathParam("containerName") String containerName,
+        @PathParam("subnetName") String subnetName) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
         ISwitchManager switchManager = null;
-        switchManager = (ISwitchManager) ServiceHelper.getInstance(
-                ISwitchManager.class, containerName, this);
+        switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
         if (switchManager == null) {
-            throw new ResourceNotFoundException(RestMessages.NOCONTAINER
-                    .toString());
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
         }
         SubnetConfig res = switchManager.getSubnetConfig(subnetName);
         if (res == null) {
-            throw new ResourceNotFoundException(RestMessages.NOSUBNET
-                    .toString());
+            throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
         } else {
             return res;
         }
     }
 
     /**
-     * Add/Update a subnet to a container
+     * Add a subnet to a container
      *
-     * @param containerName container in which we want to add/update the
-     * subnet
-     * @param subnetName that has to be added
-     * @param subnet pair default gateway IP/mask that identify the
-     * subnet being added modified
+     * @param containerName
+     *            container in which we want to add/update the subnet
+     * @param subnetName
+     *            that has to be added
+     * @param subnet
+     *            pair default gateway IP/mask that identify the subnet being
+     *            added modified
      *
      */
     @Path("/{containerName}/{subnetName}")
     @POST
-    @StatusCodes( {
-            @ResponseCode(code = 404, condition = "The containerName passed was not found"),
-            @ResponseCode(code = 404, condition = "Invalid Data passed"),
-            @ResponseCode(code = 201, condition = "Subnet added/modified"),
-            @ResponseCode(code = 500, condition = "Addition of subnet failed") })
-    public Response addSubnet(@PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName,
-            @QueryParam("subnet") String subnet) {
+    @StatusCodes({
+        @ResponseCode(code = 404, condition = "Invalid Data passed"),
+        @ResponseCode(code = 201, condition = "Subnet added"),
+        @ResponseCode(code = 500, condition = "Addition of subnet failed") })
+    public Response addSubnet(
+        @PathParam("containerName") String containerName,
+        @PathParam("subnetName") String subnetName, @QueryParam("subnet") String subnet) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
         if (subnetName == null) {
             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
         }
@@ -124,14 +154,12 @@ public class SubnetsNorthboundJAXRS {
             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
         }
         ISwitchManager switchManager = null;
-        switchManager = (ISwitchManager) ServiceHelper.getInstance(
-                ISwitchManager.class, containerName, this);
+        switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
         if (switchManager == null) {
             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
         }
 
-        SubnetConfig cfgObject = new SubnetConfig(subnetName, subnet,
-                new ArrayList<String>(0));
+        SubnetConfig cfgObject = new SubnetConfig(subnetName, subnet, new ArrayList<String>(0));
         Status status = switchManager.addSubnet(cfgObject);
         if (status.isSuccess()) {
             return Response.status(Response.Status.CREATED).build();
@@ -142,29 +170,33 @@ public class SubnetsNorthboundJAXRS {
     /**
      * Delete a subnet from a container
      *
-     * @param containerName container in which we want to delete the
-     * subnet by name
-     * @param subnetName of the subnet to be remove.
+     * @param containerName
+     *            container in which we want to delete the subnet by name
+     * @param subnetName
+     *            of the subnet to be remove.
      *
      */
     @Path("/{containerName}/{subnetName}")
     @DELETE
-    @StatusCodes( {
-            @ResponseCode(code = 404, condition = "The containerName passed was not found"),
-            @ResponseCode(code = 500, condition = "Removal of subnet failed") })
+    @StatusCodes({
+        @ResponseCode(code = 404, condition = "The containerName passed was not found"),
+        @ResponseCode(code = 500, condition = "Removal of subnet failed") })
     public Response removeSubnet(
-            @PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName) {
+        @PathParam("containerName") String containerName,
+        @PathParam("subnetName") String subnetName) {
         if (subnetName == null) {
-            throw new ResourceNotFoundException(RestMessages.INVALIDDATA
-                    .toString());
+            throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
         }
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
+
         ISwitchManager switchManager = null;
-        switchManager = (ISwitchManager) ServiceHelper.getInstance(
-                ISwitchManager.class, containerName, this);
+        switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
         if (switchManager == null) {
-            throw new ResourceNotFoundException(RestMessages.NOCONTAINER
-                    .toString());
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
         }
         Status status = switchManager.removeSubnet(subnetName);
         if (status.isSuccess()) {
@@ -173,58 +205,213 @@ public class SubnetsNorthboundJAXRS {
         throw new InternalServerErrorException(status.getDescription());
     }
 
-    /* /\** */
-    /*  * */
-    /*  * Add or remove switch ports to a subnet */
-    /*  * */
-    /*  * POST subnets/green/sw */
-    /*  * */
-    /*  * @param model */
-    /*  * @param containerName */
-    /*  * @param name */
-    /*  * @param subnet: the subnet name name */
-    /*  * @param switchports: datapath ID/port list => xx:xx:xx:xx:xx:xx:xx:xx/a,b,c-m,r-t,y */
-    /*  * @return */
-    /*  *\/ */
-    /* @RequestMapping(value = "/{containerName}/{name}", method = RequestMethod.POST) */
-    /* public View addSwitchports(Map<String, Object> model, */
-    /*         @PathVariable(value = "containerName") String containerName, */
-    /*         @PathVariable(value = "name") String name, */
-    /*         @RequestParam(value = "nodeports") String nodePorts, */
-    /*         @RequestParam(value = "action") String action) { */
-
-    /*     checkDefaultDisabled(containerName); */
-
-    /*     ISwitchManager switchManager = null; */
-    /*     try { */
-    /*         BundleContext bCtx = FrameworkUtil.getBundle(this.getClass()) */
-    /*                 .getBundleContext(); */
-
-    /*         ServiceReference[] services = bCtx.getServiceReferences( */
-    /*                 ISwitchManager.class.getName(), "(containerName=" */
-    /*                         + containerName + ")"); */
-
-    /*         if (services != null) { */
-    /*             switchManager = (ISwitchManager) bCtx.getService(services[0]); */
-    /*             logger.debug("Switch manager reference is:" + switchManager); */
-    /*         } */
-    /*     } catch (Exception e) { */
-    /*         logger.error("Switch Manager reference is NULL"); */
-    /*     } */
-
-    /*     checkContainerExists(switchManager); */
-
-    /*     String ret; */
-    /*     if (action.equals("add")) { */
-    /*         ret = switchManager.addPortsToSubnet(name, nodePorts); */
-    /*     } else if (action.equals("remove")) { */
-    /*         ret = switchManager.removePortsFromSubnet(name, nodePorts); */
-    /*     } else { */
-    /*         throw new UnsupportedMediaTypeException(RestMessages.UNKNOWNACTION */
-    /*                 .toString() */
-    /*                 + ": " + action); */
-    /*     } */
-
-    /*     return returnViewOrThrowConflicEx(model, ret); */
-    /* } */
+    /**
+     * Modify a subnet. For now only changing the port list is allowed.
+     *
+     * @param containerName
+     *            Name of the Container
+     * @param name
+     *            Name of the SubnetConfig to be modified
+     * @param subnetConfigData
+     *            the {@link SubnetConfig} structure in JSON passed as a POST
+     *            parameter
+     * @return If the operation is successful or not
+     */
+    @Path("/{containerName}/{subnetName}/modify")
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+        @ResponseCode(code = 202, condition = "Operation successful"),
+        @ResponseCode(code = 400, condition = "Invalid request, i.e., requested changing the subnet name"),
+        @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
+        @ResponseCode(code = 500, condition = "Internal server error") })
+    public Response modifySubnet(@PathParam("containerName") String containerName,
+        @PathParam("subnetName") String name,
+        @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
+
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
+        if (switchManager == null) {
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
+        }
+
+        SubnetConfig subnetConf = subnetConfigData.getValue();
+        SubnetConfig existingConf = switchManager.getSubnetConfig(name);
+
+        boolean successful = true;
+
+        // make sure that the name matches an existing subnet and we're not
+        // changing the name or subnet IP/mask
+        if (existingConf == null) {
+            // don't have a subnet by that name
+            return Response.status(Response.Status.NOT_FOUND).build();
+
+        } else if (!existingConf.getName().equals(subnetConf.getName())
+                || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
+            // can't change the name of a subnet
+            return Response.status(Response.Status.BAD_REQUEST).build();
+
+        } else {
+            // create a set for fast lookups
+            Set<String> newPorts = new HashSet<String>(subnetConf.getNodePorts());
+
+            // go through the current ports and (1) remove ports that aren't
+            // there anymore and (2) remove ports that are still there from the
+            // set of ports to add
+            for (String s : existingConf.getNodePorts()) {
+                if (newPorts.contains(s)) {
+                    newPorts.remove(s);
+                } else {
+                    Status st = switchManager.removePortsFromSubnet(name, s);
+                    successful = successful && st.isSuccess();
+                }
+            }
+
+            // add any remaining ports
+            for (String s : newPorts) {
+                Status st = switchManager.addPortsToSubnet(name, s);
+                successful = successful && st.isSuccess();
+            }
+        }
+
+        if (successful) {
+            return Response.status(Response.Status.ACCEPTED).build();
+        } else {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+    /**
+     *
+     * Add ports to a subnet
+     *
+     * @param containerName
+     *            Name of the Container
+     * @param name
+     *            Name of the SubnetConfig to be modified
+     * @param subnetConfigData
+     *            the {@link SubnetConfig} structure in JSON passed as a POST
+     *            parameter
+     * @return If the operation is successful or not
+     */
+    @Path("/{containerName}/{subnetName}/add")
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+        @ResponseCode(code = 202, condition = "Operation successful"),
+        @ResponseCode(code = 400, condition = "Invalid request"),
+        @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
+        @ResponseCode(code = 500, condition = "Internal server error") })
+    public Response addNodePorts(
+            @PathParam("containerName") String containerName,
+            @PathParam("subnetName") String name,
+            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
+
+        SubnetConfig subnetConf = subnetConfigData.getValue();
+        return addOrDeletePorts(containerName, name, subnetConf, "add");
+    }
+
+    /**
+     *
+     * Delete ports from a subnet
+     *
+     * @param containerName
+     *            Name of the Container
+     * @param name
+     *            Name of the SubnetConfig to be modified
+     * @param subnetConfigData
+     *            the {@link SubnetConfig} structure in JSON passed as a POST
+     *            parameter
+     * @return If the operation is successful or not
+     */
+    @Path("/{containerName}/{subnetName}/delete")
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+        @ResponseCode(code = 202, condition = "Operation successful"),
+        @ResponseCode(code = 400, condition = "Invalid request"),
+        @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
+        @ResponseCode(code = 500, condition = "Internal server error") })
+    public Response deleteNodePorts(
+            @PathParam("containerName") String containerName,
+            @PathParam("subnetName") String name,
+            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
+
+        SubnetConfig subnetConf = subnetConfigData.getValue();
+        return addOrDeletePorts(containerName, name, subnetConf, "delete");
+    }
+
+    /**
+    *
+    * Add/Delete ports to/from a subnet
+    *
+    * @param containerName
+    *            Name of the Container
+    * @param name
+    *            Name of the SubnetConfig to be modified
+    * @param subnetConfig
+    *            the {@link SubnetConfig} structure
+    * @param action
+    *            add or delete
+    * @return If the operation is successful or not
+    */
+    private Response addOrDeletePorts(String containerName, String name, SubnetConfig subnetConf, String action) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
+
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
+        if (switchManager == null) {
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
+        }
+
+        SubnetConfig existingConf = switchManager.getSubnetConfig(name);
+
+        // make sure that the name matches an existing subnet and we're not
+        // changing the name or subnet IP/mask
+        if (existingConf == null) {
+            // don't have a subnet by that name
+            return Response.status(Response.Status.NOT_FOUND).build();
+        } else if (!existingConf.getName().equals(subnetConf.getName())
+                || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
+            // can't change the name of a subnet
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        } else {
+            Status st;
+            boolean successful = true;
+            List<String> ports = subnetConf.getNodePorts();
+
+            if (action.equals("add")) {
+                // add new ports
+                ports.removeAll(existingConf.getNodePorts());
+                for (String port : ports) {
+                    st = switchManager.addPortsToSubnet(name, port);
+                    successful = successful && st.isSuccess();
+                }
+            } else if (action.equals("delete")) {
+                // delete existing ports
+                ports.retainAll(existingConf.getNodePorts());
+                for (String port : ports) {
+                    st = switchManager.removePortsFromSubnet(name, port);
+                    successful = successful && st.isSuccess();
+                }
+            } else {
+                return Response.status(Response.Status.BAD_REQUEST).build();
+            }
+
+            if (successful) {
+                return Response.status(Response.Status.ACCEPTED).build();
+            } else {
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+            }
+        }
+    }
 }