Merge "Remove remoterpc dead code."
[controller.git] / opendaylight / northbound / subnets / src / main / java / org / opendaylight / controller / subnets / northbound / SubnetsNorthbound.java
index a5e805d396934ec719eb18086e069a25d7326788..b6274795df9279275e1b1d7e145aa2fa3055914b 100644 (file)
@@ -19,11 +19,13 @@ 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.xml.bind.JAXBElement;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ContextResolver;
 
 import org.codehaus.enunciate.jaxrs.ResponseCode;
 import org.codehaus.enunciate.jaxrs.StatusCodes;
@@ -31,13 +33,14 @@ import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.opendaylight.controller.containermanager.IContainerManager;
 import org.opendaylight.controller.northbound.commons.RestMessages;
 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
-import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
 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.NodeConnector;
 import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
@@ -45,15 +48,38 @@ import org.opendaylight.controller.switchmanager.SubnetConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * This class provides REST APIs to manage subnets.
+ *
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default.
+ *
+ */
+
 @Path("/")
 public class SubnetsNorthbound {
     protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
 
     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) username = context.getUserPrincipal().getName();
+        if (context != null && context.getUserPrincipal() != null) {
+            username = context.getUserPrincipal().getName();
+        }
     }
 
     protected String getUserName() {
@@ -97,59 +123,77 @@ public class SubnetsNorthbound {
      *
      * @return a List of SubnetConfig
      *
-     *         <pre>
+     * <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/all
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
      *
-     * Response in XML:
+     * Response body in XML:
+     * &lt;list&gt;
      * &lt;subnetConfig&gt;
-     *    &lt;name&gt;subnet1&lt;/name&gt;
-     *    &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     *    &lt;nodePorts&gt;1/1&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;1/2&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;1/3&gt;/nodePorts&gt;
+     *    &lt;name&gt;marketingdepartment&lt;/name&gt;
+     *    &lt;subnet&gt;30.31.54.254/24&lt;/subnet&gt;
      * &lt;/subnetConfig&gt;
      * &lt;subnetConfig&gt;
-     *    &lt;name&gt;subnet2&lt;/name&gt;
-     *    &lt;subnet&gt;20.0.0.1/24&lt;/subnet&gt;
-     *    &lt;nodePorts&gt;2/1&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;2/2&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;2/3&gt;/nodePorts&gt;
+     *    &lt;name&gt;salesdepartment&lt;/name&gt;
+     *    &lt;subnet&gt;20.18.1.254/16&lt;/subnet&gt;
+     *    &lt;nodeConnectors&gt;OF|11@OF|00:00:00:aa:bb:cc:dd:ee&lt;/nodeConnectors&gt;
+     *    &lt;nodeConnectors&gt;OF|13@OF|00:00:00:aa:bb:cc:dd:ee&lt;/nodeConnectors&gt;
      * &lt;/subnetConfig&gt;
-     *
-     * Response in JSON:
-     * {
-     *  "name":"subnet1",
-     *  "subnet":"30.0.0.1/24",
-     *  "nodePorts":["1/1","1/2","1/3"]
-     * }
+     * &lt;/list&gt;
+     * Response body in JSON:
      * {
-     *  "name":"subnet2",
-     *  "subnet":"20.0.0.1/24",
-     *  "nodePorts":["2/1","2/2","2/3"]
+     *   "subnetConfig": [
+     *     {
+     *       "name": "marketingdepartment",
+     *       "subnet": "30.31.54.254/24",
+     *       "nodeConnectors": [
+     *           "OF|04@OF|00:00:00:00:00:00:00:04",
+     *           "OF|07@OF|00:00:00:00:00:00:00:07"
+     *       ]
+     *     },
+     *     {
+     *       "name":"salesdepartment",
+     *       "subnet":"20.18.1.254/16",
+     *       "nodeConnectors": [
+     *            "OF|11@OF|00:00:00:aa:bb:cc:dd:ee",
+     *            "OF|13@OF|00:00:00:aa:bb:cc:dd:ee"
+     *        ]
+     *      }
+     *   ]
      * }
+     *
      * </pre>
      */
-    @Path("/{containerName}/subnet/all")
+    @Path("/{containerName}/subnets")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 404, condition = "The containerName passed was not found"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
+        @ResponseCode(code = 404, condition = "The containerName passed was not found"),
+        @ResponseCode(code = 503, condition = "Service unavailable"),
+        @ResponseCode(code = 400, condition = "Incorrect query syntex") })
     @TypeHint(SubnetConfigs.class)
-    public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
+    public SubnetConfigs listSubnets(@PathParam("containerName") String containerName,
+        @QueryParam("_q") String queryString) {
 
         handleContainerDoesNotExist(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 = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
         if (switchManager == null) {
             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
         }
-        return new SubnetConfigs(switchManager.getSubnetsConfigList());
+        List<SubnetConfig> subnets = switchManager.getSubnetsConfigList();
+        if (queryString != null) {
+            subnets = queryContext.createQuery(queryString, SubnetConfig.class)
+                    .find(subnets);
+
+        }
+        return new SubnetConfigs(subnets);
     }
 
     /**
@@ -165,22 +209,25 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/marketingdepartment
      *
-     * Response in XML:
+     * Response body in XML:
      * &lt;subnetConfig&gt;
-     *    &lt;name&gt;subnet1&lt;/name&gt;
+     *    &lt;name&gt;marketingdepartment&lt;/name&gt;
      *    &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     *    &lt;nodePorts&gt;1/1&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;1/2&gt;/nodePorts&gt;
-     *    &lt;nodePorts&gt;1/3&gt;/nodePorts&gt;
+     *    &lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodePorts&gt;
+     *    &lt;nodeConnectors&gt;OF|3@OF|00:00:00:00:00:00:00:03&lt;/nodePorts&gt;
      * &lt;/subnetConfig&gt;
      *
-     * Response in JSON:
+     * Response body in JSON:
      * {
-     *  "name":"subnet1",
+     *  "name":"marketingdepartment",
      *  "subnet":"30.0.0.1/24",
-     *  "nodePorts":["1/1","1/2","1/3"]
+     *  "nodeConnectors":[
+     *       "OF|1@OF|00:00:00:00:00:00:00:01",
+     *       "OF|3@OF|00:00:00:00:00:00:00:03"
+     *   ]
      * }
      * </pre>
      */
@@ -188,8 +235,8 @@ public class SubnetsNorthbound {
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
+        @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
+        @ResponseCode(code = 503, condition = "Service unavailable") })
     @TypeHint(SubnetConfig.class)
     public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
             @PathParam("subnetName") String subnetName) {
@@ -200,23 +247,25 @@ public class SubnetsNorthbound {
             throw new UnauthorizedException("User is not authorized to perform this operation on container "
                     + containerName);
         }
-        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
         if (switchManager == null) {
             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
         }
         SubnetConfig res = switchManager.getSubnetConfig(subnetName);
         if (res == null) {
             throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
-        } else {
-            return res;
         }
+        return res;
     }
 
     /**
-     * Add a subnet to a container
+     * Add a subnet into the specified container context, node connectors are
+     * optional
      *
      * @param containerName
-     *            name of the container to which subnet needs to be added
+     *            name of the container context in which the subnet needs to be
+     *            added
      * @param subnetName
      *            name of new subnet to be added
      * @param subnetConfigData
@@ -227,34 +276,40 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
      *
-     * Request XML:
+     * Request body in XML:
      *  &lt;subnetConfig&gt;
-     *      &lt;name&gt;subnet1&lt;/name&gt;
-     *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     * &lt;/subnetConfig&gt;
+     *      &lt;name&gt;salesdepartment&lt;/name&gt;
+     *      &lt;subnet&gt;172.173.174.254/24&lt;/subnet&gt;
+     *      &lt;nodeConnectors&gt;OF|22@OF|00:00:11:22:33:44:55:66&lt;/nodeConnectors&gt;
+     *      &lt;nodeConnectors&gt;OF|39@OF|00:00:ab:cd:33:44:55:66&lt;/nodeConnectors&gt;
+     *  &lt;/subnetConfig&gt;
      *
-     * Request in JSON:
+     * Request body in JSON:
      * {
-     *  "name":"subnet1",
-     *  "subnet":"30.0.0.1/24"
+     *  "name":"salesdepartment",
+     *  "subnet":"172.173.174.254/24",
+     *  "nodeConnectors":[
+     *       "OF|22@OF|00:00:11:22:33:44:55:66",
+     *       "OF|39@OF|00:00:ab:cd:33:44:55:66"
+     *       ]
      * }
      * </pre>
      */
 
     @Path("/{containerName}/subnet/{subnetName}")
-    @POST
+    @PUT
     @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
-            @ResponseCode(code = 400, condition = "Invalid data passed"),
-            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
-            @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
-            @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
+        @ResponseCode(code = 400, condition = "Invalid data passed"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+        @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
+        @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
+        @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
+        @ResponseCode(code = 503, condition = "Service unavailable") })
     public Response addSubnet(@PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName,
-            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
+            @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
 
         handleContainerDoesNotExist(containerName);
 
@@ -262,52 +317,53 @@ public class SubnetsNorthbound {
             throw new UnauthorizedException("User is not authorized to perform this operation on container "
                     + containerName);
         }
-        SubnetConfig cfgObject = subnetConfigData.getValue();
+        SubnetConfig cfgObject = subnetConfigData;
         handleNameMismatch(cfgObject.getName(), subnetName);
 
-        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
         if (switchManager == null) {
             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
         }
-        Set<String> ports = cfgObject.getNodePorts();
-        SubnetConfig subnetCfg = null;
-        if (ports == null) {
-            subnetCfg = new SubnetConfig(cfgObject.getName(), cfgObject.getSubnet(), new HashSet<String>(0));
-        } else {
-            subnetCfg = cfgObject;
-        }
-
-        Status status = switchManager.addSubnet(subnetCfg);
-
+        Status status = switchManager.addSubnet(cfgObject);
         if (status.isSuccess()) {
             NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
+            if (subnetConfigData.getNodeConnectors() != null) {
+                for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
+                    NorthboundUtils.auditlog("Port", getUserName(), "added",
+                            NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway " + subnetName,
+                            containerName);
+                }
+            }
             return Response.status(Response.Status.CREATED).build();
         }
         return NorthboundUtils.getResponse(status);
     }
 
     /**
-     * Delete a subnet from a container
+     * Delete a subnet from the specified container context
      *
      * @param containerName
-     *            name of the container from which subnet needs to be removed
+     *            name of the container in which subnet needs to be removed
      * @param subnetName
      *            name of new subnet to be deleted
      * @return Response as dictated by the HTTP Response Status code
      *
-     *         <pre>
+     * <pre>
      * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/engdepartment
      *
      * </pre>
      */
     @Path("/{containerName}/subnet/{subnetName}")
     @DELETE
     @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
-            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 404, condition = "The containerName passed was not found"),
-            @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+        @ResponseCode(code = 404, condition = "The containerName passed was not found"),
+        @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
+        @ResponseCode(code = 503, condition = "Service unavailable") })
     public Response removeSubnet(@PathParam("containerName") String containerName,
             @PathParam("subnetName") String subnetName) {
 
@@ -318,7 +374,8 @@ public class SubnetsNorthbound {
                     + containerName);
         }
 
-        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
+                this);
         if (switchManager == null) {
             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
         }
@@ -331,164 +388,55 @@ public class SubnetsNorthbound {
     }
 
     /**
-     * Modify a subnet. For now only changing the port list is allowed.
+     * Modify a subnet. Replace the existing subnet with the new specified one.
+     * For now only port list modification is allowed. If the respective subnet
+     * configuration does not exist this call is equivalent to a subnet
+     * creation.
      *
      * @param containerName
-     *            Name of the Container
+     *            Name of the Container context
      * @param subnetName
-     *            Name of the SubnetConfig to be modified
+     *            Name of the subnet to be modified
      * @param subnetConfigData
-     *            the {@link SubnetConfig} structure in request body
-     *            parameter
-     * @return If the operation is successful or not
-     *
-     *         <pre>
-     * Example:
-     *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
-     *
-     *  Request in XML:
-     *  &lt;subnetConfig&gt;
-     *      &lt;name&gt;subnet1&lt;/name&gt;
-     *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     *      &lt;nodePorts&gt;1/1&lt;/nodePorts&gt;
-     *      &lt;nodePorts&gt;1/2&lt;/nodePorts&gt;
-     *      &lt;nodePorts&gt;1/3&lt;/nodePorts&gt;
-     * &lt;/subnetConfig&gt;
-     *
-     * Request in JSON:
-     * {
-     *  "name":"subnet1",
-     *  "subnet":"30.0.0.1/24",
-     *  "nodePorts":["1/1","1/2","1/3"]
-     * }
-     * </pre>
-     */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
-    @PUT
-    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    @StatusCodes({
-            @ResponseCode(code = 200, condition = "Ports replaced successfully"),
-            @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
-            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
-            @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
-            @ResponseCode(code = 500, condition = "Internal server error: Modify ports failed"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
-    public Response modifySubnet(@PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName,
-            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
-
-        handleContainerDoesNotExist(containerName);
-
-        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
-            throw new UnauthorizedException("User is not authorized to perform this operation on container "
-                    + containerName);
-        }
-        handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
-
-        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
-                this);
-        if (switchManager == null) {
-            throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
-        }
-
-        SubnetConfig subnetConf = subnetConfigData.getValue();
-        SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
-
-        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
-            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(subnetName, s);
-                    successful = successful && st.isSuccess();
-                }
-            }
-
-            // add any remaining ports
-            for (String s : newPorts) {
-                Status st = switchManager.addPortsToSubnet(subnetName, s);
-                successful = successful && st.isSuccess();
-                if (successful) {
-                    NorthboundUtils.auditlog("Subnet Gateway", username, "added", s + " to " + subnetName,
-                            containerName);
-                }
-            }
-        }
-
-        if (successful) {
-            return Response.status(Response.Status.OK).build();
-        }
-        throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
-    }
-
-    /**
-     * Add ports to a subnet in the container
-     *
-     * @param containerName
-     *            name of the container that has the subnet to which node ports
-     *            need to be added
-     * @param subnetName
-     *            name of subnet to which node ports need to be added
-     * @param SubnetConfig
-     *            the {@link SubnetConfig} structure in request body
+     *            the {@link SubnetConfig} structure in request body parameter
      * @return Response as dictated by the HTTP Response Status code
      *
      *         <pre>
      * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
      *
-     * Request XML:
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
+     *
+     *  Request body in XML:
      *  &lt;subnetConfig&gt;
-     *      &lt;name&gt;subnet1&lt;/name&gt;
-     *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     *      &lt;nodePorts&gt;1/1&lt;/nodePorts&gt;
-     *      &lt;nodePorts&gt;1/2&lt;/nodePorts&gt;
-     *      &lt;nodePorts&gt;1/3&lt;/nodePorts&gt;
-     * &lt;/subnetConfig&gt;
+     *      &lt;name&gt;salesdepartment&lt;/name&gt;
+     *      &lt;subnet&gt;172.173.174.254/24&lt;/subnet&gt;
+     *      &lt;nodeConnectors&gt;OF|22@OF|00:00:11:22:33:44:55:66&lt;/nodeConnectors&gt;
+     *      &lt;nodeConnectors&gt;OF|39@OF|00:00:ab:cd:33:44:55:66&lt;/nodeConnectors&gt;
+     *  &lt;/subnetConfig&gt;
      *
-     * Request in JSON:
+     * Request body in JSON:
      * {
-     *  "name":"subnet1",
-     *  "subnet":"30.0.0.1/24",
-     *  "nodePorts":["1/1","1/2","1/3"]
+     *  "name":"salesdepartment",
+     *  "subnet":"172.173.174.254/24",
+     *  "nodeConnectors":[
+     *      "OF|22@OF|00:00:11:22:33:44:55:66",
+     *      "OF|39@OF|00:00:ab:cd:33:44:55:66"
+     *  ]
      * }
-     *
      * </pre>
      */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
+    @Path("/{containerName}/subnet/{subnetName}")
     @POST
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    @StatusCodes({
-            @ResponseCode(code = 200, condition = "Added node ports to subnet successfully"),
-            @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
-            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
-            @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
-            @ResponseCode(code = 500, condition = "Internal server error : Port add failed"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
-    public Response addNodePorts(@PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName,
-            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Configuration replaced successfully"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+        @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
+        @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
+        @ResponseCode(code = 500, condition = "Internal server error: Modify subnet failed"),
+        @ResponseCode(code = 503, condition = "Service unavailable") })
+    public Response modifySubnet(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
+            @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
 
         handleContainerDoesNotExist(containerName);
 
@@ -496,9 +444,7 @@ public class SubnetsNorthbound {
             throw new UnauthorizedException("User is not authorized to perform this operation on container "
                     + containerName);
         }
-        handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
-
-        SubnetConfig subnetConf = subnetConfigData.getValue();
+        handleNameMismatch(subnetConfigData.getName(), subnetName);
 
         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
                 this);
@@ -506,137 +452,47 @@ public class SubnetsNorthbound {
             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
         }
 
+        // Need to check this until Status does not return a CREATED status code
         SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
 
-        // 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();
-        }
-        Status st;
-        boolean successful = true;
-        Set<String> ports = subnetConf.getNodePorts();
-
-        if (ports == null || ports.isEmpty()) {
-            throw new BadRequestException(RestMessages.INVALIDDATA.toString());
-        }
-
-        // add new ports only
-        if (existingConf.getNodePorts() != null) {
-            ports.removeAll(existingConf.getNodePorts());
-        }
-        for (String port : ports) {
-            st = switchManager.addPortsToSubnet(subnetName, port);
-            successful = successful && st.isSuccess();
-            if (successful) {
-                NorthboundUtils.auditlog("Subnet Gateway", username, "added", st + " to " + subnetName, containerName);
-            }
-        }
-        if (successful) {
-            return Response.status(Response.Status.OK).build();
-        }
-        throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
-    }
-
-    /**
-     * Delete ports from a subnet in the container
-     *
-     * @param containerName
-     *            name of the container that has the subnet from which node
-     *            ports need to be deleted
-     * @param subnetName
-     *            name of subnet from which node ports need to be deleted
-     * @param subnetConfigData
-     *            SubnetConfig object to be deleted
-     * @return Response as dictated by the HTTP Response Status code
-     *
-     *         <pre>
-     * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
-     *
-     * Request XML:
-     *  &lt;subnetConfig&gt;
-     *      &lt;name&gt;subnet3&lt;/name&gt;
-     *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
-     *      &lt;nodePorts&gt;1/1,1/2,1/3&lt;/nodePorts&gt;
-     * &lt;/subnetConfig&gt;
-     *
-     * Request in JSON:
-     * { "name" : "subnet1",
-     *   "subnet" : "30.0.0.1/24",
-     *    nodePorts : ["1/1,1/2,1/3"]}
-     *
-     * </pre>
-     */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
-    @DELETE
-    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-    @StatusCodes({
-            @ResponseCode(code = 204, condition = "No content"),
-            @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
-            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
-            @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
-            @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
-            @ResponseCode(code = 500, condition = "Internal server error : Delete node ports failed"),
-            @ResponseCode(code = 503, condition = "Service unavailable") })
-    public Response deleteNodePorts(@PathParam("containerName") String containerName,
-            @PathParam("subnetName") String subnetName,
-            @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
-
-        handleContainerDoesNotExist(containerName);
-
-        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
-            throw new UnauthorizedException("User is not authorized to perform this operation on container "
-                    + containerName);
-        }
-        handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
-
-        SubnetConfig subnetConf = subnetConfigData.getValue();
+        Status status = switchManager.modifySubnet(subnetConfigData);
 
-        if (subnetConf.getNodePorts() == null || subnetConf.getNodePorts().isEmpty()) {
-            throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : invalid node ports");
-        }
-
-        ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
-                this);
-        if (switchManager == null) {
-            throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
-        }
-
-        SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
+        if (status.isSuccess()) {
+            if (existingConf == null) {
+                NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
+                if (subnetConfigData.getNodeConnectors() != null) {
+                    for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
+                        NorthboundUtils.auditlog("Port", getUserName(), "added",
+                                NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway" + subnetName,
+                                containerName);
+                    }
+                }
+                return Response.created(uriInfo.getRequestUri()).build();
+            } else {
+                Set<NodeConnector> existingNCList = existingConf.getNodeConnectors();
 
-        // 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();
-        }
-        Status st;
-        boolean successful = true;
-        Set<String> ports = subnetConf.getNodePorts();
-
-        // delete existing ports
-        ports.retainAll(existingConf.getNodePorts());
-        for (String port : ports) {
-            st = switchManager.removePortsFromSubnet(subnetName, port);
-            successful = successful && st.isSuccess();
-            if (successful) {
-                NorthboundUtils.auditlog("Subnet Gateway", username, "removed", st + " from " + subnetName,
-                        containerName);
+                if (existingNCList == null) {
+                    existingNCList = new HashSet<NodeConnector>(0);
+                }
+                if (subnetConfigData.getNodeConnectors() != null) {
+                    for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
+                        if (!existingNCList.contains(port)) {
+                            NorthboundUtils.auditlog("Port", getUserName(), "added",
+                                    NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway "
+                                            + subnetName, containerName);
+                        }
+                    }
+                }
+                for (NodeConnector port : existingNCList) {
+                    if (!subnetConfigData.getNodeConnectors().contains(port)) {
+                        NorthboundUtils
+                                .auditlog("Port", getUserName(), "removed",
+                                        NorthboundUtils.getPortName(port, switchManager) + " from Subnet Gateway "
+                                                + subnetName, containerName);
+                    }
+                }
             }
         }
-        if (successful) {
-            return Response.status(Response.Status.NO_CONTENT).build();
-        }
-        throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
+        return NorthboundUtils.getResponse(status);
     }
 }