2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.subnets.northbound;
10 import java.util.HashSet;
11 import java.util.List;
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;
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;
49 * This class provides REST APIs to manage subnets.
53 * Authentication scheme : <b>HTTP Basic</b><br>
54 * Authentication realm : <b>opendaylight</b><br>
55 * Transport : <b>HTTP and HTTPS</b><br>
57 * HTTPS Authentication is disabled by default.
62 public class SubnetsNorthbound {
63 protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
65 private String username;
68 public void setSecurityContext(SecurityContext context) {
69 if (context != null && context.getUserPrincipal() != null) {
70 username = context.getUserPrincipal().getName();
74 protected String getUserName() {
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());
85 List<String> containerNames = containerManager.getContainerNames();
86 for (String cName : containerNames) {
87 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
92 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
95 private void handleNameMismatch(String name, String nameinURL) {
96 if (name == null || nameinURL == null) {
97 throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
100 if (name.equals(nameinURL)) {
103 throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
104 + " : Name in URL does not match the name in request body");
108 * List all the subnets in a given container
110 * @param containerName
111 * container in which we want to query the subnets
113 * @return a List of SubnetConfig
119 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
121 * Response body in XML:
123 * <subnetConfig>
124 * <name>marketingdepartment</name>
125 * <subnet>30.31.54.254/24</subnet>
126 * </subnetConfig>
127 * <subnetConfig>
128 * <name>salesdepartment</name>
129 * <subnet>20.18.1.254/16</subnet>
130 * <nodeConnectors>OF|11@OF|00:00:00:aa:bb:cc:dd:ee</nodeConnectors>
131 * <nodeConnectors>OF|13@OF|00:00:00:aa:bb:cc:dd:ee</nodeConnectors>
132 * </subnetConfig>
134 * Response body in JSON:
138 * "name": "marketingdepartment",
139 * "subnet": "30.31.54.254/24",
140 * "nodeConnectors": [
141 * "OF|04@OF|00:00:00:00:00:00:00:04",
142 * "OF|07@OF|00:00:00:00:00:00:00:07"
146 * "name":"salesdepartment",
147 * "subnet":"20.18.1.254/16",
148 * "nodeConnectors": [
149 * "OF|11@OF|00:00:00:aa:bb:cc:dd:ee",
150 * "OF|13@OF|00:00:00:aa:bb:cc:dd:ee"
158 @Path("/{containerName}/subnets")
160 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
161 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
162 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
163 @ResponseCode(code = 503, condition = "Service unavailable") })
164 @TypeHint(SubnetConfigs.class)
165 public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
167 handleContainerDoesNotExist(containerName);
168 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
169 throw new UnauthorizedException("User is not authorized to perform this operation on container "
172 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
174 if (switchManager == null) {
175 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
177 return new SubnetConfigs(switchManager.getSubnetsConfigList());
181 * List the configuration of a subnet in a given container
183 * @param containerName
184 * container in which we want to query the subnet
186 * of the subnet being queried
188 * @return SubnetConfig
194 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/marketingdepartment
196 * Response body in XML:
197 * <subnetConfig>
198 * <name>marketingdepartment</name>
199 * <subnet>30.0.0.1/24</subnet>
200 * <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodePorts>
201 * <nodeConnectors>OF|3@OF|00:00:00:00:00:00:00:03</nodePorts>
202 * </subnetConfig>
204 * Response body in JSON:
206 * "name":"marketingdepartment",
207 * "subnet":"30.0.0.1/24",
209 * "OF|1@OF|00:00:00:00:00:00:00:01",
210 * "OF|3@OF|00:00:00:00:00:00:00:03"
215 @Path("/{containerName}/subnet/{subnetName}")
217 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
218 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
219 @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
220 @ResponseCode(code = 503, condition = "Service unavailable") })
221 @TypeHint(SubnetConfig.class)
222 public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
223 @PathParam("subnetName") String subnetName) {
225 handleContainerDoesNotExist(containerName);
227 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
228 throw new UnauthorizedException("User is not authorized to perform this operation on container "
231 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
233 if (switchManager == null) {
234 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
236 SubnetConfig res = switchManager.getSubnetConfig(subnetName);
238 throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
244 * Add a subnet into the specified container context, node connectors are
247 * @param containerName
248 * name of the container context in which the subnet needs to be
251 * name of new subnet to be added
252 * @param subnetConfigData
253 * the {@link SubnetConfig} structure in request body
255 * @return Response as dictated by the HTTP Response Status code
261 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
263 * Request body in XML:
264 * <subnetConfig>
265 * <name>salesdepartment</name>
266 * <subnet>172.173.174.254/24</subnet>
267 * <nodeConnectors>OF|22@OF|00:00:11:22:33:44:55:66</nodeConnectors>
268 * <nodeConnectors>OF|39@OF|00:00:ab:cd:33:44:55:66</nodeConnectors>
269 * </subnetConfig>
271 * Request body in JSON:
273 * "name":"salesdepartment",
274 * "subnet":"172.173.174.254/24",
276 * "OF|22@OF|00:00:11:22:33:44:55:66",
277 * "OF|39@OF|00:00:ab:cd:33:44:55:66"
283 @Path("/{containerName}/subnet/{subnetName}")
285 @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
286 @ResponseCode(code = 400, condition = "Invalid data passed"),
287 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
288 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
289 @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
290 @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
291 @ResponseCode(code = 503, condition = "Service unavailable") })
292 public Response addSubnet(@PathParam("containerName") String containerName,
293 @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
295 handleContainerDoesNotExist(containerName);
297 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
298 throw new UnauthorizedException("User is not authorized to perform this operation on container "
301 SubnetConfig cfgObject = subnetConfigData;
302 handleNameMismatch(cfgObject.getName(), subnetName);
304 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
306 if (switchManager == null) {
307 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
309 Status status = switchManager.addSubnet(cfgObject);
310 if (status.isSuccess()) {
311 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
312 if (subnetConfigData.getNodeConnectors() != null) {
313 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
314 NorthboundUtils.auditlog("Port", getUserName(), "added",
315 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway " + subnetName,
319 return Response.status(Response.Status.CREATED).build();
321 return NorthboundUtils.getResponse(status);
325 * Delete a subnet from the specified container context
327 * @param containerName
328 * name of the container in which subnet needs to be removed
330 * name of new subnet to be deleted
331 * @return Response as dictated by the HTTP Response Status code
337 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/engdepartment
341 @Path("/{containerName}/subnet/{subnetName}")
343 @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
344 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
345 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
346 @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
347 @ResponseCode(code = 503, condition = "Service unavailable") })
348 public Response removeSubnet(@PathParam("containerName") String containerName,
349 @PathParam("subnetName") String subnetName) {
351 handleContainerDoesNotExist(containerName);
353 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
354 throw new UnauthorizedException("User is not authorized to perform this operation on container "
358 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
360 if (switchManager == null) {
361 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
363 Status status = switchManager.removeSubnet(subnetName);
364 if (status.isSuccess()) {
365 NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
366 return Response.status(Response.Status.NO_CONTENT).build();
368 return NorthboundUtils.getResponse(status);
372 * Modify a subnet. Replace the existing subnet with the new specified one.
373 * For now only port list modification is allowed. If the respective subnet
374 * configuration does not exist this call is equivalent to a subnet
377 * @param containerName
378 * Name of the Container context
380 * Name of the subnet to be modified
381 * @param subnetConfigData
382 * the {@link SubnetConfig} structure in request body parameter
383 * @return Response as dictated by the HTTP Response Status code
389 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
391 * Request body in XML:
392 * <subnetConfig>
393 * <name>salesdepartment</name>
394 * <subnet>172.173.174.254/24</subnet>
395 * <nodeConnectors>OF|22@OF|00:00:11:22:33:44:55:66</nodeConnectors>
396 * <nodeConnectors>OF|39@OF|00:00:ab:cd:33:44:55:66</nodeConnectors>
397 * </subnetConfig>
399 * Request body in JSON:
401 * "name":"salesdepartment",
402 * "subnet":"172.173.174.254/24",
404 * "OF|22@OF|00:00:11:22:33:44:55:66",
405 * "OF|39@OF|00:00:ab:cd:33:44:55:66"
410 @Path("/{containerName}/subnet/{subnetName}")
412 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
413 @StatusCodes({ @ResponseCode(code = 200, condition = "Configuration replaced successfully"),
414 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
415 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
416 @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
417 @ResponseCode(code = 500, condition = "Internal server error: Modify subnet failed"),
418 @ResponseCode(code = 503, condition = "Service unavailable") })
419 public Response modifySubnet(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
420 @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
422 handleContainerDoesNotExist(containerName);
424 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
425 throw new UnauthorizedException("User is not authorized to perform this operation on container "
428 handleNameMismatch(subnetConfigData.getName(), subnetName);
430 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
432 if (switchManager == null) {
433 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
436 // Need to check this until Status does not return a CREATED status code
437 SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
439 Status status = switchManager.modifySubnet(subnetConfigData);
441 if (status.isSuccess()) {
442 if (existingConf == null) {
443 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
444 if (subnetConfigData.getNodeConnectors() != null) {
445 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
446 NorthboundUtils.auditlog("Port", getUserName(), "added",
447 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway" + subnetName,
451 return Response.created(uriInfo.getRequestUri()).build();
453 Set<NodeConnector> existingNCList = existingConf.getNodeConnectors();
455 if (existingNCList == null) {
456 existingNCList = new HashSet<NodeConnector>(0);
458 if (subnetConfigData.getNodeConnectors() != null) {
459 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
460 if (!existingNCList.contains(port)) {
461 NorthboundUtils.auditlog("Port", getUserName(), "added",
462 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway "
463 + subnetName, containerName);
467 for (NodeConnector port : existingNCList) {
468 if (!subnetConfigData.getNodeConnectors().contains(port)) {
470 .auditlog("Port", getUserName(), "removed",
471 NorthboundUtils.getPortName(port, switchManager) + " from Subnet Gateway "
472 + subnetName, containerName);
477 return NorthboundUtils.getResponse(status);