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