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