Merge "toaster-it: add missing version for maven-paxexam-plugin"
[controller.git] / opendaylight / northbound / subnets / src / main / java / org / opendaylight / controller / subnets / northbound / SubnetsNorthbound.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.subnets.northbound;
9
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Set;
13
14 import javax.ws.rs.Consumes;
15 import javax.ws.rs.DELETE;
16 import javax.ws.rs.GET;
17 import javax.ws.rs.POST;
18 import javax.ws.rs.PUT;
19 import javax.ws.rs.Path;
20 import javax.ws.rs.PathParam;
21 import javax.ws.rs.Produces;
22 import javax.ws.rs.core.Context;
23 import javax.ws.rs.core.MediaType;
24 import javax.ws.rs.core.Response;
25 import javax.ws.rs.core.SecurityContext;
26 import javax.xml.bind.JAXBElement;
27
28 import org.codehaus.enunciate.jaxrs.ResponseCode;
29 import org.codehaus.enunciate.jaxrs.StatusCodes;
30 import org.codehaus.enunciate.jaxrs.TypeHint;
31 import org.opendaylight.controller.containermanager.IContainerManager;
32 import org.opendaylight.controller.northbound.commons.RestMessages;
33 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
34 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
36 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
37 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
38 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
39 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
40 import org.opendaylight.controller.sal.authorization.Privilege;
41 import org.opendaylight.controller.sal.utils.ServiceHelper;
42 import org.opendaylight.controller.sal.utils.Status;
43 import org.opendaylight.controller.switchmanager.ISwitchManager;
44 import org.opendaylight.controller.switchmanager.SubnetConfig;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 @Path("/")
49 public class SubnetsNorthbound {
50     protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
51
52     private String username;
53
54     @Context
55     public void setSecurityContext(SecurityContext context) {
56         if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
57     }
58
59     protected String getUserName() {
60         return username;
61     }
62
63     private void handleContainerDoesNotExist(String containerName) {
64         IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
65                 IContainerManager.class, this);
66         if (containerManager == null) {
67             throw new ServiceUnavailableException("Container " + RestMessages.NOCONTAINER.toString());
68         }
69
70         List<String> containerNames = containerManager.getContainerNames();
71         for (String cName : containerNames) {
72             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
73                 return;
74             }
75         }
76
77         throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
78     }
79
80     private void handleNameMismatch(String name, String nameinURL) {
81         if (name == null || nameinURL == null) {
82             throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
83         }
84
85         if (name.equals(nameinURL)) {
86             return;
87         }
88         throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
89                 + " : Name in URL does not match the name in request body");
90     }
91
92     /**
93      * List all the subnets in a given container
94      *
95      * @param containerName
96      *            container in which we want to query the subnets
97      *
98      * @return a List of SubnetConfig
99      *
100      *         <pre>
101      * Example:
102      *
103      * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/all
104      *
105      * Response in XML:
106      * &lt;subnetConfig&gt;
107      *    &lt;name&gt;subnet1&lt;/name&gt;
108      *    &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
109      *    &lt;nodePorts&gt;1/1&gt;/nodePorts&gt;
110      *    &lt;nodePorts&gt;1/2&gt;/nodePorts&gt;
111      *    &lt;nodePorts&gt;1/3&gt;/nodePorts&gt;
112      * &lt;/subnetConfig&gt;
113      * &lt;subnetConfig&gt;
114      *    &lt;name&gt;subnet2&lt;/name&gt;
115      *    &lt;subnet&gt;20.0.0.1/24&lt;/subnet&gt;
116      *    &lt;nodePorts&gt;2/1&gt;/nodePorts&gt;
117      *    &lt;nodePorts&gt;2/2&gt;/nodePorts&gt;
118      *    &lt;nodePorts&gt;2/3&gt;/nodePorts&gt;
119      * &lt;/subnetConfig&gt;
120      *
121      * Response in JSON:
122      * {
123      *  "name":"subnet1",
124      *  "subnet":"30.0.0.1/24",
125      *  "nodePorts":["1/1","1/2","1/3"]
126      * }
127      * {
128      *  "name":"subnet2",
129      *  "subnet":"20.0.0.1/24",
130      *  "nodePorts":["2/1","2/2","2/3"]
131      * }
132      * </pre>
133      */
134     @Path("/{containerName}/subnet/all")
135     @GET
136     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
137     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
138             @ResponseCode(code = 404, condition = "The containerName passed was not found"),
139             @ResponseCode(code = 503, condition = "Service unavailable") })
140     @TypeHint(SubnetConfigs.class)
141     public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
142
143         handleContainerDoesNotExist(containerName);
144         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
145             throw new UnauthorizedException("User is not authorized to perform this operation on container "
146                     + containerName);
147         }
148          ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
149         if (switchManager == null) {
150             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
151         }
152         return new SubnetConfigs(switchManager.getSubnetsConfigList());
153     }
154
155     /**
156      * List the configuration of a subnet in a given container
157      *
158      * @param containerName
159      *            container in which we want to query the subnet
160      * @param subnetName
161      *            of the subnet being queried
162      *
163      * @return SubnetConfig
164      *
165      *         <pre>
166      * Example:
167      *
168      * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
169      *
170      * Response in XML:
171      * &lt;subnetConfig&gt;
172      *    &lt;name&gt;subnet1&lt;/name&gt;
173      *    &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
174      *    &lt;nodePorts&gt;1/1&gt;/nodePorts&gt;
175      *    &lt;nodePorts&gt;1/2&gt;/nodePorts&gt;
176      *    &lt;nodePorts&gt;1/3&gt;/nodePorts&gt;
177      * &lt;/subnetConfig&gt;
178      *
179      * Response in JSON:
180      * {
181      *  "name":"subnet1",
182      *  "subnet":"30.0.0.1/24",
183      *  "nodePorts":["1/1","1/2","1/3"]
184      * }
185      * </pre>
186      */
187     @Path("/{containerName}/subnet/{subnetName}")
188     @GET
189     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
190     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
191             @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
192             @ResponseCode(code = 503, condition = "Service unavailable") })
193     @TypeHint(SubnetConfig.class)
194     public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
195             @PathParam("subnetName") String subnetName) {
196
197         handleContainerDoesNotExist(containerName);
198
199         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
200             throw new UnauthorizedException("User is not authorized to perform this operation on container "
201                     + containerName);
202         }
203         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
204         if (switchManager == null) {
205             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
206         }
207         SubnetConfig res = switchManager.getSubnetConfig(subnetName);
208         if (res == null) {
209             throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
210         } else {
211             return res;
212         }
213     }
214
215     /**
216      * Add a subnet to a container
217      *
218      * @param containerName
219      *            name of the container to which subnet needs to be added
220      * @param subnetName
221      *            name of new subnet to be added
222      * @param subnetConfigData
223      *            the {@link SubnetConfig} structure in request body
224      *
225      * @return Response as dictated by the HTTP Response Status code
226      *
227      *         <pre>
228      * Example:
229      *
230      * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
231      *
232      * Request XML:
233      *  &lt;subnetConfig&gt;
234      *      &lt;name&gt;subnet1&lt;/name&gt;
235      *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
236      * &lt;/subnetConfig&gt;
237      *
238      * Request in JSON:
239      * {
240      *  "name":"subnet1",
241      *  "subnet":"30.0.0.1/24"
242      * }
243      * </pre>
244      */
245
246     @Path("/{containerName}/subnet/{subnetName}")
247     @POST
248     @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
249             @ResponseCode(code = 400, condition = "Invalid data passed"),
250             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
251             @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
252             @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
253             @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
254             @ResponseCode(code = 503, condition = "Service unavailable") })
255     public Response addSubnet(@PathParam("containerName") String containerName,
256             @PathParam("subnetName") String subnetName,
257             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
258
259         handleContainerDoesNotExist(containerName);
260
261         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
262             throw new UnauthorizedException("User is not authorized to perform this operation on container "
263                     + containerName);
264         }
265         SubnetConfig cfgObject = subnetConfigData.getValue();
266         handleNameMismatch(cfgObject.getName(), subnetName);
267
268         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
269         if (switchManager == null) {
270             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
271         }
272         Set<String> ports = cfgObject.getNodePorts();
273         SubnetConfig subnetCfg = null;
274         if (ports == null) {
275             subnetCfg = new SubnetConfig(cfgObject.getName(), cfgObject.getSubnet(), new HashSet<String>(0));
276         } else {
277             subnetCfg = cfgObject;
278         }
279
280         Status status = switchManager.addSubnet(subnetCfg);
281
282         if (status.isSuccess()) {
283             NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
284             return Response.status(Response.Status.CREATED).build();
285         }
286         return NorthboundUtils.getResponse(status);
287     }
288
289     /**
290      * Delete a subnet from a container
291      *
292      * @param containerName
293      *            name of the container from which subnet needs to be removed
294      * @param subnetName
295      *            name of new subnet to be deleted
296      * @return Response as dictated by the HTTP Response Status code
297      *
298      *         <pre>
299      * Example:
300      *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
301      *
302      * </pre>
303      */
304     @Path("/{containerName}/subnet/{subnetName}")
305     @DELETE
306     @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
307             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
308             @ResponseCode(code = 404, condition = "The containerName passed was not found"),
309             @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
310             @ResponseCode(code = 503, condition = "Service unavailable") })
311     public Response removeSubnet(@PathParam("containerName") String containerName,
312             @PathParam("subnetName") String subnetName) {
313
314         handleContainerDoesNotExist(containerName);
315
316         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
317             throw new UnauthorizedException("User is not authorized to perform this operation on container "
318                     + containerName);
319         }
320
321         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
322         if (switchManager == null) {
323             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
324         }
325         Status status = switchManager.removeSubnet(subnetName);
326         if (status.isSuccess()) {
327             NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
328             return Response.status(Response.Status.NO_CONTENT).build();
329         }
330         return NorthboundUtils.getResponse(status);
331     }
332
333     /**
334      * Modify a subnet. For now only changing the port list is allowed.
335      *
336      * @param containerName
337      *            Name of the Container
338      * @param subnetName
339      *            Name of the SubnetConfig to be modified
340      * @param subnetConfigData
341      *            the {@link SubnetConfig} structure in request body
342      *            parameter
343      * @return If the operation is successful or not
344      *
345      *         <pre>
346      * Example:
347      *
348      * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
349      *
350      *  Request in XML:
351      *  &lt;subnetConfig&gt;
352      *      &lt;name&gt;subnet1&lt;/name&gt;
353      *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
354      *      &lt;nodePorts&gt;1/1&lt;/nodePorts&gt;
355      *      &lt;nodePorts&gt;1/2&lt;/nodePorts&gt;
356      *      &lt;nodePorts&gt;1/3&lt;/nodePorts&gt;
357      * &lt;/subnetConfig&gt;
358      *
359      * Request in JSON:
360      * {
361      *  "name":"subnet1",
362      *  "subnet":"30.0.0.1/24",
363      *  "nodePorts":["1/1","1/2","1/3"]
364      * }
365      * </pre>
366      */
367     @Path("/{containerName}/subnet/{subnetName}/node-ports")
368     @PUT
369     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
370     @StatusCodes({
371             @ResponseCode(code = 200, condition = "Ports replaced successfully"),
372             @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
373             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
374             @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
375             @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
376             @ResponseCode(code = 500, condition = "Internal server error: Modify ports failed"),
377             @ResponseCode(code = 503, condition = "Service unavailable") })
378     public Response modifySubnet(@PathParam("containerName") String containerName,
379             @PathParam("subnetName") String subnetName,
380             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
381
382         handleContainerDoesNotExist(containerName);
383
384         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
385             throw new UnauthorizedException("User is not authorized to perform this operation on container "
386                     + containerName);
387         }
388         handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
389
390         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
391                 this);
392         if (switchManager == null) {
393             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
394         }
395
396         SubnetConfig subnetConf = subnetConfigData.getValue();
397         SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
398
399         boolean successful = true;
400
401         // make sure that the name matches an existing subnet and we're not
402         // changing the name or subnet IP/mask
403         if (existingConf == null) {
404             // don't have a subnet by that name
405             return Response.status(Response.Status.NOT_FOUND).build();
406
407         } else if (!existingConf.getName().equals(subnetConf.getName())
408                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
409             // can't change the name of a subnet
410             Response.status(Response.Status.BAD_REQUEST).build();
411         } else {
412             // create a set for fast lookups
413             Set<String> newPorts = new HashSet<String>(subnetConf.getNodePorts());
414
415             // go through the current ports and (1) remove ports that aren't
416             // there anymore and (2) remove ports that are still there from the
417             // set of ports to add
418             for (String s : existingConf.getNodePorts()) {
419                 if (newPorts.contains(s)) {
420                     newPorts.remove(s);
421                 } else {
422                     Status st = switchManager.removePortsFromSubnet(subnetName, s);
423                     successful = successful && st.isSuccess();
424                 }
425             }
426
427             // add any remaining ports
428             for (String s : newPorts) {
429                 Status st = switchManager.addPortsToSubnet(subnetName, s);
430                 successful = successful && st.isSuccess();
431                 if (successful) {
432                     NorthboundUtils.auditlog("Subnet Gateway", username, "added", s + " to " + subnetName,
433                             containerName);
434                 }
435             }
436         }
437
438         if (successful) {
439             return Response.status(Response.Status.OK).build();
440         }
441         throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
442     }
443
444     /**
445      * Add ports to a subnet in the container
446      *
447      * @param containerName
448      *            name of the container that has the subnet to which node ports
449      *            need to be added
450      * @param subnetName
451      *            name of subnet to which node ports need to be added
452      * @param SubnetConfig
453      *            the {@link SubnetConfig} structure in request body
454      * @return Response as dictated by the HTTP Response Status code
455      *
456      *         <pre>
457      * Example:
458      *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
459      *
460      * Request XML:
461      *  &lt;subnetConfig&gt;
462      *      &lt;name&gt;subnet1&lt;/name&gt;
463      *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
464      *      &lt;nodePorts&gt;1/1&lt;/nodePorts&gt;
465      *      &lt;nodePorts&gt;1/2&lt;/nodePorts&gt;
466      *      &lt;nodePorts&gt;1/3&lt;/nodePorts&gt;
467      * &lt;/subnetConfig&gt;
468      *
469      * Request in JSON:
470      * {
471      *  "name":"subnet1",
472      *  "subnet":"30.0.0.1/24",
473      *  "nodePorts":["1/1","1/2","1/3"]
474      * }
475      *
476      * </pre>
477      */
478     @Path("/{containerName}/subnet/{subnetName}/node-ports")
479     @POST
480     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
481     @StatusCodes({
482             @ResponseCode(code = 200, condition = "Added node ports to subnet successfully"),
483             @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
484             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
485             @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
486             @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
487             @ResponseCode(code = 500, condition = "Internal server error : Port add failed"),
488             @ResponseCode(code = 503, condition = "Service unavailable") })
489     public Response addNodePorts(@PathParam("containerName") String containerName,
490             @PathParam("subnetName") String subnetName,
491             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
492
493         handleContainerDoesNotExist(containerName);
494
495         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
496             throw new UnauthorizedException("User is not authorized to perform this operation on container "
497                     + containerName);
498         }
499         handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
500
501         SubnetConfig subnetConf = subnetConfigData.getValue();
502
503         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
504                 this);
505         if (switchManager == null) {
506             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
507         }
508
509         SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
510
511         // make sure that the name matches an existing subnet and we're not
512         // changing the name or subnet IP/mask
513         if (existingConf == null) {
514             // don't have a subnet by that name
515             return Response.status(Response.Status.NOT_FOUND).build();
516         } else if (!existingConf.getName().equals(subnetConf.getName())
517                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
518             // can't change the name of a subnet
519             return Response.status(Response.Status.BAD_REQUEST).build();
520         }
521         Status st;
522         boolean successful = true;
523         Set<String> ports = subnetConf.getNodePorts();
524
525         if (ports == null || ports.isEmpty()) {
526             throw new BadRequestException(RestMessages.INVALIDDATA.toString());
527         }
528
529         // add new ports only
530         if (existingConf.getNodePorts() != null) {
531             ports.removeAll(existingConf.getNodePorts());
532         }
533         for (String port : ports) {
534             st = switchManager.addPortsToSubnet(subnetName, port);
535             successful = successful && st.isSuccess();
536             if (successful) {
537                 NorthboundUtils.auditlog("Subnet Gateway", username, "added", st + " to " + subnetName, containerName);
538             }
539         }
540         if (successful) {
541             return Response.status(Response.Status.OK).build();
542         }
543         throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
544     }
545
546     /**
547      * Delete ports from a subnet in the container
548      *
549      * @param containerName
550      *            name of the container that has the subnet from which node
551      *            ports need to be deleted
552      * @param subnetName
553      *            name of subnet from which node ports need to be deleted
554      * @param subnetConfigData
555      *            SubnetConfig object to be deleted
556      * @return Response as dictated by the HTTP Response Status code
557      *
558      *         <pre>
559      * Example:
560      *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
561      *
562      * Request XML:
563      *  &lt;subnetConfig&gt;
564      *      &lt;name&gt;subnet3&lt;/name&gt;
565      *      &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
566      *      &lt;nodePorts&gt;1/1,1/2,1/3&lt;/nodePorts&gt;
567      * &lt;/subnetConfig&gt;
568      *
569      * Request in JSON:
570      * { "name" : "subnet1",
571      *   "subnet" : "30.0.0.1/24",
572      *    nodePorts : ["1/1,1/2,1/3"]}
573      *
574      * </pre>
575      */
576     @Path("/{containerName}/subnet/{subnetName}/node-ports")
577     @DELETE
578     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
579     @StatusCodes({
580             @ResponseCode(code = 204, condition = "No content"),
581             @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
582             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
583             @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
584             @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
585             @ResponseCode(code = 500, condition = "Internal server error : Delete node ports failed"),
586             @ResponseCode(code = 503, condition = "Service unavailable") })
587     public Response deleteNodePorts(@PathParam("containerName") String containerName,
588             @PathParam("subnetName") String subnetName,
589             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
590
591         handleContainerDoesNotExist(containerName);
592
593         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
594             throw new UnauthorizedException("User is not authorized to perform this operation on container "
595                     + containerName);
596         }
597         handleNameMismatch(subnetConfigData.getValue().getName(), subnetName);
598
599         SubnetConfig subnetConf = subnetConfigData.getValue();
600
601         if (subnetConf.getNodePorts() == null || subnetConf.getNodePorts().isEmpty()) {
602             throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : invalid node ports");
603         }
604
605         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
606                 this);
607         if (switchManager == null) {
608             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
609         }
610
611         SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
612
613         // make sure that the name matches an existing subnet and we're not
614         // changing the name or subnet IP/mask
615         if (existingConf == null) {
616             // don't have a subnet by that name
617             return Response.status(Response.Status.NOT_FOUND).build();
618         } else if (!existingConf.getName().equals(subnetConf.getName())
619                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
620             // can't change the name of a subnet
621             return Response.status(Response.Status.BAD_REQUEST).build();
622         }
623         Status st;
624         boolean successful = true;
625         Set<String> ports = subnetConf.getNodePorts();
626
627         // delete existing ports
628         ports.retainAll(existingConf.getNodePorts());
629         for (String port : ports) {
630             st = switchManager.removePortsFromSubnet(subnetName, port);
631             successful = successful && st.isSuccess();
632             if (successful) {
633                 NorthboundUtils.auditlog("Subnet Gateway", username, "removed", st + " from " + subnetName,
634                         containerName);
635             }
636         }
637         if (successful) {
638             return Response.status(Response.Status.NO_CONTENT).build();
639         }
640         throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
641     }
642 }