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