Merge "Few debug enhancements to debug hashing issues"
[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 import java.util.List;
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.PUT;
19 import javax.ws.rs.Path;
20 import javax.ws.rs.PathParam;
21 import javax.ws.rs.Produces;
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.ws.rs.core.UriInfo;
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.containermanager.IContainerManager;
32 import org.opendaylight.controller.northbound.commons.RestMessages;
33 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
34 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
36 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
37 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
38 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
39 import org.opendaylight.controller.sal.authorization.Privilege;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.utils.ServiceHelper;
42 import org.opendaylight.controller.sal.utils.Status;
43 import org.opendaylight.controller.switchmanager.ISwitchManager;
44 import org.opendaylight.controller.switchmanager.SubnetConfig;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * This class provides REST APIs to manage subnets.
50  *
51  * <br>
52  * <br>
53  * Authentication scheme : <b>HTTP Basic</b><br>
54  * Authentication realm : <b>opendaylight</b><br>
55  * Transport : <b>HTTP and HTTPS</b><br>
56  * <br>
57  * HTTPS Authentication is disabled by default.
58  *
59  */
60
61 @Path("/")
62 public class SubnetsNorthbound {
63     protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
64
65     private String username;
66
67     @Context
68     public void setSecurityContext(SecurityContext context) {
69         if (context != null && context.getUserPrincipal() != null) {
70             username = context.getUserPrincipal().getName();
71         }
72     }
73
74     protected String getUserName() {
75         return username;
76     }
77
78     private void handleContainerDoesNotExist(String containerName) {
79         IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
80                 IContainerManager.class, this);
81         if (containerManager == null) {
82             throw new ServiceUnavailableException("Container " + RestMessages.NOCONTAINER.toString());
83         }
84
85         List<String> containerNames = containerManager.getContainerNames();
86         for (String cName : containerNames) {
87             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
88                 return;
89             }
90         }
91
92         throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
93     }
94
95     private void handleNameMismatch(String name, String nameinURL) {
96         if (name == null || nameinURL == null) {
97             throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
98         }
99
100         if (name.equals(nameinURL)) {
101             return;
102         }
103         throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
104                 + " : Name in URL does not match the name in request body");
105     }
106
107     /**
108      * List all the subnets in a given container
109      *
110      * @param containerName
111      *            container in which we want to query the subnets
112      *
113      * @return a List of SubnetConfig
114      *
115      *         <pre>
116      * Example:
117      *
118      * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
119      *
120      * Response in XML:
121      * &lt;subnetConfig&gt;
122      *    &lt;name&gt;marketingdepartment&lt;/name&gt;
123      *    &lt;subnet&gt;30.31.54.254/24&lt;/subnet&gt;
124      * &lt;/subnetConfig&gt;
125      * &lt;subnetConfig&gt;
126      *    &lt;name&gt;salesdepartment&lt;/name&gt;
127      *    &lt;subnet&gt;20.18.1.254/16&lt;/subnet&gt;
128      *    &lt;nodeConnectors&gt;0F|11@OF|00:00:00:aa:bb:cc:dd:ee&gt;/nodeConnectors&gt;
129      *    &lt;nodeConnectors&gt;0F|13@OF|00:00:00:aa:bb:cc:dd:ee&gt;/nodeConnectors&gt;
130      * &lt;/subnetConfig&gt;
131      *
132      * Response in JSON:
133      * {
134      *  "name":"marketingdepartment",
135      *  "subnet":"30.31.54.254/24",
136      * }
137      * {
138      *  "name":"salesdepartment",
139      *  "subnet":"20.18.1.254/16",
140      *  "nodeConnectors":["0F|11@OF|00:00:00:aa:bb:cc:dd:ee", "0F|13@OF|00:00:00:aa:bb:cc:dd:ee"]
141      * }
142      * </pre>
143      */
144     @Path("/{containerName}/subnets")
145     @GET
146     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
147     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
148         @ResponseCode(code = 404, condition = "The containerName passed was not found"),
149         @ResponseCode(code = 503, condition = "Service unavailable") })
150     @TypeHint(SubnetConfigs.class)
151     public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
152
153         handleContainerDoesNotExist(containerName);
154         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
155             throw new UnauthorizedException("User is not authorized to perform this operation on container "
156                     + containerName);
157         }
158         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
159                 this);
160         if (switchManager == null) {
161             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
162         }
163         return new SubnetConfigs(switchManager.getSubnetsConfigList());
164     }
165
166     /**
167      * List the configuration of a subnet in a given container
168      *
169      * @param containerName
170      *            container in which we want to query the subnet
171      * @param subnetName
172      *            of the subnet being queried
173      *
174      * @return SubnetConfig
175      *
176      *         <pre>
177      * Example:
178      *
179      * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/marketingdepartment
180      *
181      * Response in XML:
182      * &lt;subnetConfig&gt;
183      *    &lt;name&gt;marketingdepartment&lt;/name&gt;
184      *    &lt;subnet&gt;30.0.0.1/24&lt;/subnet&gt;
185      *    &lt;nodeConnectors&gt;0F|1@OF|00:00:11:22:33:44:55:66&gt;/nodePorts&gt;
186      *    &lt;nodeConnectors&gt;0F|3@OF|00:00:11:22:33:44:55:66&gt;/nodePorts&gt;
187      * &lt;/subnetConfig&gt;
188      *
189      * Response in JSON:
190      * {
191      *  "name":"marketingdepartment",
192      *  "subnet":"30.0.0.1/24",
193      *  "nodeConnectors":["0F|1@OF|00:00:11:22:33:44:55:66", "0F|3@OF|00:00:11:22:33:44:55:66"]
194      * }
195      * </pre>
196      */
197     @Path("/{containerName}/subnet/{subnetName}")
198     @GET
199     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
200     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
201         @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
202         @ResponseCode(code = 503, condition = "Service unavailable") })
203     @TypeHint(SubnetConfig.class)
204     public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
205             @PathParam("subnetName") String subnetName) {
206
207         handleContainerDoesNotExist(containerName);
208
209         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
210             throw new UnauthorizedException("User is not authorized to perform this operation on container "
211                     + containerName);
212         }
213         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
214                 this);
215         if (switchManager == null) {
216             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
217         }
218         SubnetConfig res = switchManager.getSubnetConfig(subnetName);
219         if (res == null) {
220             throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
221         }
222         return res;
223     }
224
225     /**
226      * Add a subnet into the specified container context, node connectors are
227      * optional
228      *
229      * @param containerName
230      *            name of the container context in which the subnet needs to be
231      *            added
232      * @param subnetName
233      *            name of new subnet to be added
234      * @param subnetConfigData
235      *            the {@link SubnetConfig} structure in request body
236      *
237      * @return Response as dictated by the HTTP Response Status code
238      *
239      *         <pre>
240      * Example:
241      *
242      * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
243      *
244      * Request XML:
245      *  &lt;subnetConfig&gt;
246      *      &lt;name&gt;salesdepartment&lt;/name&gt;
247      *      &lt;subnet&gt;172.173.174.254/24&lt;/subnet&gt;
248      *      &lt;nodeConnectors&gt;0F|22@OF|00:00:11:22:33:44:55:66&gt;/nodeConnectors&gt;
249      *      &lt;nodeConnectors&gt;0F|39@OF|00:00:ab:cd:33:44:55:66&gt;/nodeConnectors&gt;
250      *  &lt;/subnetConfig&gt;
251      *
252      * Request in JSON:
253      * {
254      *  "name":"salesdepartment",
255      *  "subnet":"172.173.174.254/24"
256      *  "nodeConnectors":["0F|22@OF|00:00:11:22:33:44:55:66", "0F|39@OF|00:00:ab:cd:33:44:55:66"]
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, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
272
273         handleContainerDoesNotExist(containerName);
274
275         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
276             throw new UnauthorizedException("User is not authorized to perform this operation on container "
277                     + containerName);
278         }
279         SubnetConfig cfgObject = subnetConfigData;
280         handleNameMismatch(cfgObject.getName(), subnetName);
281
282         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
283                 this);
284         if (switchManager == null) {
285             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
286         }
287         Status status = switchManager.addSubnet(cfgObject);
288         if (status.isSuccess()) {
289             NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
290             if (subnetConfigData.getNodeConnectors() != null) {
291                 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
292                     NorthboundUtils.auditlog("Port", getUserName(), "added",
293                             NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway " + subnetName,
294                             containerName);
295                 }
296             }
297             return Response.status(Response.Status.CREATED).build();
298         }
299         return NorthboundUtils.getResponse(status);
300     }
301
302     /**
303      * Delete a subnet from the specified container context
304      *
305      * @param containerName
306      *            name of the container in which subnet needs to be removed
307      * @param subnetName
308      *            name of new subnet to be deleted
309      * @return Response as dictated by the HTTP Response Status code
310      *
311      *         <pre>
312      * Example:
313      *            Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/engdepartment
314      *
315      * </pre>
316      */
317     @Path("/{containerName}/subnet/{subnetName}")
318     @DELETE
319     @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
320         @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
321         @ResponseCode(code = 404, condition = "The containerName passed was not found"),
322         @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
323         @ResponseCode(code = 503, condition = "Service unavailable") })
324     public Response removeSubnet(@PathParam("containerName") String containerName,
325             @PathParam("subnetName") String subnetName) {
326
327         handleContainerDoesNotExist(containerName);
328
329         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
330             throw new UnauthorizedException("User is not authorized to perform this operation on container "
331                     + containerName);
332         }
333
334         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
335                 this);
336         if (switchManager == null) {
337             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
338         }
339         Status status = switchManager.removeSubnet(subnetName);
340         if (status.isSuccess()) {
341             NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
342             return Response.status(Response.Status.NO_CONTENT).build();
343         }
344         return NorthboundUtils.getResponse(status);
345     }
346
347     /**
348      * Modify a subnet. Replace the existing subnet with the new specified one.
349      * For now only port list modification is allowed. If the respective subnet
350      * configuration does not exist this call is equivalent to a subnet
351      * creation.
352      *
353      * @param containerName
354      *            Name of the Container context
355      * @param subnetName
356      *            Name of the subnet to be modified
357      * @param subnetConfigData
358      *            the {@link SubnetConfig} structure in request body parameter
359      * @return Response as dictated by the HTTP Response Status code
360      *
361      *         <pre>
362      * Example:
363      *
364      * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
365      *
366      *  Request in XML:
367      *  &lt;subnetConfig&gt;
368      *      &lt;name&gt;salesdepartment&lt;/name&gt;
369      *      &lt;subnet&gt;172.173.174.254/24&lt;/subnet&gt;
370      *      &lt;nodeConnectors&gt;0F|22@OF|00:00:11:22:33:44:55:66&gt;/nodeConnectors&gt;
371      *      &lt;nodeConnectors&gt;0F|39@OF|00:00:ab:cd:33:44:55:66&gt;/nodeConnectors&gt;
372      *  &lt;/subnetConfig&gt;
373      *
374      * Request in JSON:
375      * {
376      *  "name":"salesdepartment",
377      *  "subnet":"172.173.174.254/24"
378      *  "nodeConnectors":["0F|22@OF|00:00:11:22:33:44:55:66", "0F|39@OF|00:00:ab:cd:33:44:55:66"]
379      * }
380      * </pre>
381      */
382     @Path("/{containerName}/subnet/{subnetName}")
383     @POST
384     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
385     @StatusCodes({ @ResponseCode(code = 200, condition = "Configuration replaced successfully"),
386         @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
387         @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
388         @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
389         @ResponseCode(code = 500, condition = "Internal server error: Modify subnet failed"),
390         @ResponseCode(code = 503, condition = "Service unavailable") })
391     public Response modifySubnet(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
392             @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
393
394         handleContainerDoesNotExist(containerName);
395
396         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
397             throw new UnauthorizedException("User is not authorized to perform this operation on container "
398                     + containerName);
399         }
400         handleNameMismatch(subnetConfigData.getName(), subnetName);
401
402         ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
403                 this);
404         if (switchManager == null) {
405             throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
406         }
407
408         // Need to check this until Status does not return a CREATED status code
409         SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
410
411         Status status = switchManager.modifySubnet(subnetConfigData);
412
413         if (status.isSuccess()) {
414             if (existingConf == null) {
415                 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
416                 if (subnetConfigData.getNodeConnectors() != null) {
417                     for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
418                         NorthboundUtils.auditlog("Port", getUserName(), "added",
419                                 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway" + subnetName,
420                                 containerName);
421                     }
422                 }
423                 return Response.created(uriInfo.getRequestUri()).build();
424             } else {
425                 Set<NodeConnector> existingNCList = existingConf.getNodeConnectors();
426
427                 if (existingNCList == null) {
428                     existingNCList = new HashSet<NodeConnector>(0);
429                 }
430                 if (subnetConfigData.getNodeConnectors() != null) {
431                     for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
432                         if (!existingNCList.contains(port)) {
433                             NorthboundUtils.auditlog("Port", getUserName(), "added",
434                                     NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway "
435                                             + subnetName, containerName);
436                         }
437                     }
438                 }
439                 for (NodeConnector port : existingNCList) {
440                     if (!subnetConfigData.getNodeConnectors().contains(port)) {
441                         NorthboundUtils
442                                 .auditlog("Port", getUserName(), "removed",
443                                         NorthboundUtils.getPortName(port, switchManager) + " from Subnet Gateway "
444                                                 + subnetName, containerName);
445                     }
446                 }
447             }
448         }
449         return NorthboundUtils.getResponse(status);
450     }
451 }