REST APIs to add/delete ports to/from a subnet.
[controller.git] / opendaylight / northbound / subnets / src / main / java / org / opendaylight / controller / subnets / northbound / SubnetsNorthboundJAXRS.java
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.ArrayList;
11 import java.util.HashSet;
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.Path;
20 import javax.ws.rs.PathParam;
21 import javax.ws.rs.Produces;
22 import javax.ws.rs.QueryParam;
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.northbound.commons.RestMessages;
33 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
34 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
35 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
36 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
37 import org.opendaylight.controller.sal.authorization.Privilege;
38 import org.opendaylight.controller.sal.utils.ServiceHelper;
39 import org.opendaylight.controller.sal.utils.Status;
40 import org.opendaylight.controller.switchmanager.ISwitchManager;
41 import org.opendaylight.controller.switchmanager.SubnetConfig;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Path("/")
46 public class SubnetsNorthboundJAXRS {
47     protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthboundJAXRS.class);
48
49     private String username;
50
51     @Context
52     public void setSecurityContext(SecurityContext context) {
53         username = context.getUserPrincipal().getName();
54     }
55
56     protected String getUserName() {
57         return username;
58     }
59
60     /**
61      * List all the subnets in a given container
62      *
63      * @param containerName
64      *            container in which we want to query the subnets
65      *
66      * @return a List of SubnetConfig
67      */
68     @Path("/{containerName}")
69     @GET
70     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
71     @StatusCodes({ @ResponseCode(code = 404, condition = "The containerName passed was not found") })
72     @TypeHint(SubnetConfigs.class)
73     public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
74         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
75             throw new UnauthorizedException("User is not authorized to perform this operation on container "
76                     + containerName);
77         }
78         ISwitchManager switchManager = null;
79         switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
80         if (switchManager == null) {
81             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
82         }
83         return new SubnetConfigs(switchManager.getSubnetsConfigList());
84     }
85
86     /**
87      * List the configuration of a subnet in a given container
88      *
89      * @param containerName
90      *            container in which we want to query the subnet
91      * @param subnetName
92      *            of the subnet being queried
93      *
94      * @return a SubnetConfig
95      */
96     @Path("/{containerName}/{subnetName}")
97     @GET
98     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
99     @StatusCodes({
100         @ResponseCode(code = 404, condition = "The containerName passed was not found"),
101         @ResponseCode(code = 404, condition = "Subnet does not exist") })
102         @TypeHint(SubnetConfig.class)
103     public SubnetConfig listSubnet(
104         @PathParam("containerName") String containerName,
105         @PathParam("subnetName") String subnetName) {
106
107         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
108             throw new UnauthorizedException("User is not authorized to perform this operation on container "
109                     + containerName);
110         }
111         ISwitchManager switchManager = null;
112         switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
113         if (switchManager == null) {
114             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
115         }
116         SubnetConfig res = switchManager.getSubnetConfig(subnetName);
117         if (res == null) {
118             throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
119         } else {
120             return res;
121         }
122     }
123
124     /**
125      * Add a subnet to a container
126      *
127      * @param containerName
128      *            container in which we want to add/update the subnet
129      * @param subnetName
130      *            that has to be added
131      * @param subnet
132      *            pair default gateway IP/mask that identify the subnet being
133      *            added modified
134      *
135      */
136     @Path("/{containerName}/{subnetName}")
137     @POST
138     @StatusCodes({
139         @ResponseCode(code = 404, condition = "Invalid Data passed"),
140         @ResponseCode(code = 201, condition = "Subnet added"),
141         @ResponseCode(code = 500, condition = "Addition of subnet failed") })
142     public Response addSubnet(
143         @PathParam("containerName") String containerName,
144         @PathParam("subnetName") String subnetName, @QueryParam("subnet") String subnet) {
145
146         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
147             throw new UnauthorizedException("User is not authorized to perform this operation on container "
148                     + containerName);
149         }
150         if (subnetName == null) {
151             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
152         }
153         if (subnet == null) {
154             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
155         }
156         ISwitchManager switchManager = null;
157         switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
158         if (switchManager == null) {
159             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
160         }
161
162         SubnetConfig cfgObject = new SubnetConfig(subnetName, subnet, new ArrayList<String>(0));
163         Status status = switchManager.addSubnet(cfgObject);
164         if (status.isSuccess()) {
165             return Response.status(Response.Status.CREATED).build();
166         }
167         throw new InternalServerErrorException(status.getDescription());
168     }
169
170     /**
171      * Delete a subnet from a container
172      *
173      * @param containerName
174      *            container in which we want to delete the subnet by name
175      * @param subnetName
176      *            of the subnet to be remove.
177      *
178      */
179     @Path("/{containerName}/{subnetName}")
180     @DELETE
181     @StatusCodes({
182         @ResponseCode(code = 404, condition = "The containerName passed was not found"),
183         @ResponseCode(code = 500, condition = "Removal of subnet failed") })
184     public Response removeSubnet(
185         @PathParam("containerName") String containerName,
186         @PathParam("subnetName") String subnetName) {
187         if (subnetName == null) {
188             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
189         }
190
191         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
192             throw new UnauthorizedException("User is not authorized to perform this operation on container "
193                     + containerName);
194         }
195
196         ISwitchManager switchManager = null;
197         switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
198         if (switchManager == null) {
199             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
200         }
201         Status status = switchManager.removeSubnet(subnetName);
202         if (status.isSuccess()) {
203             return Response.status(Response.Status.OK).build();
204         }
205         throw new InternalServerErrorException(status.getDescription());
206     }
207
208     /**
209      * Modify a subnet. For now only changing the port list is allowed.
210      *
211      * @param containerName
212      *            Name of the Container
213      * @param name
214      *            Name of the SubnetConfig to be modified
215      * @param subnetConfigData
216      *            the {@link SubnetConfig} structure in JSON passed as a POST
217      *            parameter
218      * @return If the operation is successful or not
219      */
220     @Path("/{containerName}/{subnetName}/modify")
221     @POST
222     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
223     @StatusCodes({
224         @ResponseCode(code = 202, condition = "Operation successful"),
225         @ResponseCode(code = 400, condition = "Invalid request, i.e., requested changing the subnet name"),
226         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
227         @ResponseCode(code = 500, condition = "Internal server error") })
228     public Response modifySubnet(@PathParam("containerName") String containerName,
229         @PathParam("subnetName") String name,
230         @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
231
232         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
233             throw new UnauthorizedException("User is not authorized to perform this operation on container "
234                     + containerName);
235         }
236
237         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
238                 this);
239         if (switchManager == null) {
240             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
241         }
242
243         SubnetConfig subnetConf = subnetConfigData.getValue();
244         SubnetConfig existingConf = switchManager.getSubnetConfig(name);
245
246         boolean successful = true;
247
248         // make sure that the name matches an existing subnet and we're not
249         // changing the name or subnet IP/mask
250         if (existingConf == null) {
251             // don't have a subnet by that name
252             return Response.status(Response.Status.NOT_FOUND).build();
253
254         } else if (!existingConf.getName().equals(subnetConf.getName())
255                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
256             // can't change the name of a subnet
257             return Response.status(Response.Status.BAD_REQUEST).build();
258
259         } else {
260             // create a set for fast lookups
261             Set<String> newPorts = new HashSet<String>(subnetConf.getNodePorts());
262
263             // go through the current ports and (1) remove ports that aren't
264             // there anymore and (2) remove ports that are still there from the
265             // set of ports to add
266             for (String s : existingConf.getNodePorts()) {
267                 if (newPorts.contains(s)) {
268                     newPorts.remove(s);
269                 } else {
270                     Status st = switchManager.removePortsFromSubnet(name, s);
271                     successful = successful && st.isSuccess();
272                 }
273             }
274
275             // add any remaining ports
276             for (String s : newPorts) {
277                 Status st = switchManager.addPortsToSubnet(name, s);
278                 successful = successful && st.isSuccess();
279             }
280         }
281
282         if (successful) {
283             return Response.status(Response.Status.ACCEPTED).build();
284         } else {
285             return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
286         }
287     }
288
289     /**
290      *
291      * Add ports to a subnet
292      *
293      * @param containerName
294      *            Name of the Container
295      * @param name
296      *            Name of the SubnetConfig to be modified
297      * @param subnetConfigData
298      *            the {@link SubnetConfig} structure in JSON passed as a POST
299      *            parameter
300      * @return If the operation is successful or not
301      */
302     @Path("/{containerName}/{subnetName}/add")
303     @POST
304     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
305     @StatusCodes({
306         @ResponseCode(code = 202, condition = "Operation successful"),
307         @ResponseCode(code = 400, condition = "Invalid request"),
308         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
309         @ResponseCode(code = 500, condition = "Internal server error") })
310     public Response addNodePorts(
311             @PathParam("containerName") String containerName,
312             @PathParam("subnetName") String name,
313             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
314
315         SubnetConfig subnetConf = subnetConfigData.getValue();
316         return addOrDeletePorts(containerName, name, subnetConf, "add");
317     }
318
319     /**
320      *
321      * Delete ports from a subnet
322      *
323      * @param containerName
324      *            Name of the Container
325      * @param name
326      *            Name of the SubnetConfig to be modified
327      * @param subnetConfigData
328      *            the {@link SubnetConfig} structure in JSON passed as a POST
329      *            parameter
330      * @return If the operation is successful or not
331      */
332     @Path("/{containerName}/{subnetName}/delete")
333     @POST
334     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
335     @StatusCodes({
336         @ResponseCode(code = 202, condition = "Operation successful"),
337         @ResponseCode(code = 400, condition = "Invalid request"),
338         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
339         @ResponseCode(code = 500, condition = "Internal server error") })
340     public Response deleteNodePorts(
341             @PathParam("containerName") String containerName,
342             @PathParam("subnetName") String name,
343             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
344
345         SubnetConfig subnetConf = subnetConfigData.getValue();
346         return addOrDeletePorts(containerName, name, subnetConf, "delete");
347     }
348
349     /**
350     *
351     * Add/Delete ports to/from a subnet
352     *
353     * @param containerName
354     *            Name of the Container
355     * @param name
356     *            Name of the SubnetConfig to be modified
357     * @param subnetConfig
358     *            the {@link SubnetConfig} structure
359     * @param action
360     *            add or delete
361     * @return If the operation is successful or not
362     */
363     private Response addOrDeletePorts(String containerName, String name, SubnetConfig subnetConf, String action) {
364
365         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
366             throw new UnauthorizedException("User is not authorized to perform this operation on container "
367                     + containerName);
368         }
369
370         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
371                 this);
372         if (switchManager == null) {
373             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
374         }
375
376         SubnetConfig existingConf = switchManager.getSubnetConfig(name);
377
378         // make sure that the name matches an existing subnet and we're not
379         // changing the name or subnet IP/mask
380         if (existingConf == null) {
381             // don't have a subnet by that name
382             return Response.status(Response.Status.NOT_FOUND).build();
383         } else if (!existingConf.getName().equals(subnetConf.getName())
384                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
385             // can't change the name of a subnet
386             return Response.status(Response.Status.BAD_REQUEST).build();
387         } else {
388             Status st;
389             boolean successful = true;
390             List<String> ports = subnetConf.getNodePorts();
391
392             if (action.equals("add")) {
393                 // add new ports
394                 ports.removeAll(existingConf.getNodePorts());
395                 for (String port : ports) {
396                     st = switchManager.addPortsToSubnet(name, port);
397                     successful = successful && st.isSuccess();
398                 }
399             } else if (action.equals("delete")) {
400                 // delete existing ports
401                 ports.retainAll(existingConf.getNodePorts());
402                 for (String port : ports) {
403                     st = switchManager.removePortsFromSubnet(name, port);
404                     successful = successful && st.isSuccess();
405                 }
406             } else {
407                 return Response.status(Response.Status.BAD_REQUEST).build();
408             }
409
410             if (successful) {
411                 return Response.status(Response.Status.ACCEPTED).build();
412             } else {
413                 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
414             }
415         }
416     }
417 }