Change 'Nodes Learnt' to 'Nodes Learned'
[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             NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
164             return Response.status(Response.Status.CREATED).build();
165         }
166         throw new InternalServerErrorException(status.getDescription());
167     }
168
169     /**
170      * Delete a subnet from a container
171      *
172      * @param containerName
173      *            container in which we want to delete the subnet by name
174      * @param subnetName
175      *            of the subnet to be remove.
176      *
177      */
178     @Path("/{containerName}/{subnetName}")
179     @DELETE
180     @StatusCodes({
181         @ResponseCode(code = 404, condition = "The containerName passed was not found"),
182         @ResponseCode(code = 500, condition = "Removal of subnet failed") })
183     public Response removeSubnet(
184         @PathParam("containerName") String containerName,
185         @PathParam("subnetName") String subnetName) {
186         if (subnetName == null) {
187             throw new ResourceNotFoundException(RestMessages.INVALIDDATA.toString());
188         }
189
190         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
191             throw new UnauthorizedException("User is not authorized to perform this operation on container "
192                     + containerName);
193         }
194
195         ISwitchManager switchManager = null;
196         switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
197         if (switchManager == null) {
198             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
199         }
200         Status status = switchManager.removeSubnet(subnetName);
201         if (status.isSuccess()) {
202             NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
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                 if(successful){
280                     NorthboundUtils.auditlog("Subnet Gateway", username, "added", s +" to "+name, containerName);
281                 }
282             }
283         }
284
285         if (successful) {
286             return Response.status(Response.Status.ACCEPTED).build();
287         } else {
288             return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
289         }
290     }
291
292     /**
293      *
294      * Add ports to a subnet
295      *
296      * @param containerName
297      *            Name of the Container
298      * @param name
299      *            Name of the SubnetConfig to be modified
300      * @param subnetConfigData
301      *            the {@link SubnetConfig} structure in JSON passed as a POST
302      *            parameter
303      * @return If the operation is successful or not
304      */
305     @Path("/{containerName}/{subnetName}/add")
306     @POST
307     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
308     @StatusCodes({
309         @ResponseCode(code = 202, condition = "Operation successful"),
310         @ResponseCode(code = 400, condition = "Invalid request"),
311         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
312         @ResponseCode(code = 500, condition = "Internal server error") })
313     public Response addNodePorts(
314             @PathParam("containerName") String containerName,
315             @PathParam("subnetName") String name,
316             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
317
318         SubnetConfig subnetConf = subnetConfigData.getValue();
319         return addOrDeletePorts(containerName, name, subnetConf, "add");
320     }
321
322     /**
323      *
324      * Delete ports from a subnet
325      *
326      * @param containerName
327      *            Name of the Container
328      * @param name
329      *            Name of the SubnetConfig to be modified
330      * @param subnetConfigData
331      *            the {@link SubnetConfig} structure in JSON passed as a POST
332      *            parameter
333      * @return If the operation is successful or not
334      */
335     @Path("/{containerName}/{subnetName}/delete")
336     @POST
337     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
338     @StatusCodes({
339         @ResponseCode(code = 202, condition = "Operation successful"),
340         @ResponseCode(code = 400, condition = "Invalid request"),
341         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
342         @ResponseCode(code = 500, condition = "Internal server error") })
343     public Response deleteNodePorts(
344             @PathParam("containerName") String containerName,
345             @PathParam("subnetName") String name,
346             @TypeHint(SubnetConfig.class) JAXBElement<SubnetConfig> subnetConfigData) {
347
348         SubnetConfig subnetConf = subnetConfigData.getValue();
349         return addOrDeletePorts(containerName, name, subnetConf, "delete");
350     }
351
352     /**
353     *
354     * Add/Delete ports to/from a subnet
355     *
356     * @param containerName
357     *            Name of the Container
358     * @param name
359     *            Name of the SubnetConfig to be modified
360     * @param subnetConfig
361     *            the {@link SubnetConfig} structure
362     * @param action
363     *            add or delete
364     * @return If the operation is successful or not
365     */
366     private Response addOrDeletePorts(String containerName, String name, SubnetConfig subnetConf, String action) {
367
368         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
369             throw new UnauthorizedException("User is not authorized to perform this operation on container "
370                     + containerName);
371         }
372
373         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
374                 this);
375         if (switchManager == null) {
376             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
377         }
378
379         SubnetConfig existingConf = switchManager.getSubnetConfig(name);
380
381         // make sure that the name matches an existing subnet and we're not
382         // changing the name or subnet IP/mask
383         if (existingConf == null) {
384             // don't have a subnet by that name
385             return Response.status(Response.Status.NOT_FOUND).build();
386         } else if (!existingConf.getName().equals(subnetConf.getName())
387                 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
388             // can't change the name of a subnet
389             return Response.status(Response.Status.BAD_REQUEST).build();
390         } else {
391             Status st;
392             boolean successful = true;
393             Set<String> ports = subnetConf.getNodePorts();
394
395             if (action.equals("add")) {
396                 // add new ports
397                 ports.removeAll(existingConf.getNodePorts());
398                 for (String port : ports) {
399                     st = switchManager.addPortsToSubnet(name, port);
400                     successful = successful && st.isSuccess();
401                     if(successful){
402                         NorthboundUtils.auditlog("Subnet Gateway", username, "added",  st +" to "+name, containerName);
403                     }
404                 }
405             } else if (action.equals("delete")) {
406                 // delete existing ports
407                 ports.retainAll(existingConf.getNodePorts());
408                 for (String port : ports) {
409                     st = switchManager.removePortsFromSubnet(name, port);
410                     successful = successful && st.isSuccess();
411                     if(successful){
412                         NorthboundUtils.auditlog("Subnet Gateway", username, "removed",  st +" from "+name, containerName);
413                     }
414                 }
415             } else {
416                 return Response.status(Response.Status.BAD_REQUEST).build();
417             }
418
419             if (successful) {
420                 return Response.status(Response.Status.ACCEPTED).build();
421             } else {
422                 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
423             }
424         }
425     }
426 }