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.QueryParam;
23 import javax.ws.rs.core.Context;
24 import javax.ws.rs.core.MediaType;
25 import javax.ws.rs.core.Response;
26 import javax.ws.rs.core.SecurityContext;
27 import javax.ws.rs.core.UriInfo;
28 import javax.ws.rs.ext.ContextResolver;
30 import org.codehaus.enunciate.jaxrs.ResponseCode;
31 import org.codehaus.enunciate.jaxrs.StatusCodes;
32 import org.codehaus.enunciate.jaxrs.TypeHint;
33 import org.opendaylight.controller.containermanager.IContainerManager;
34 import org.opendaylight.controller.northbound.commons.RestMessages;
35 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
36 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
37 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
38 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
39 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
40 import org.opendaylight.controller.northbound.commons.query.QueryContext;
41 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
42 import org.opendaylight.controller.sal.authorization.Privilege;
43 import org.opendaylight.controller.sal.core.NodeConnector;
44 import org.opendaylight.controller.sal.utils.ServiceHelper;
45 import org.opendaylight.controller.sal.utils.Status;
46 import org.opendaylight.controller.switchmanager.ISwitchManager;
47 import org.opendaylight.controller.switchmanager.SubnetConfig;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * This class provides REST APIs to manage subnets.
56 * Authentication scheme : <b>HTTP Basic</b><br>
57 * Authentication realm : <b>opendaylight</b><br>
58 * Transport : <b>HTTP and HTTPS</b><br>
60 * HTTPS Authentication is disabled by default.
65 public class SubnetsNorthbound {
66 protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
68 private String username;
69 private QueryContext queryContext;
72 public void setQueryContext(ContextResolver<QueryContext> queryCtxResolver) {
73 if (queryCtxResolver != null) {
74 queryContext = queryCtxResolver.getContext(QueryContext.class);
79 public void setSecurityContext(SecurityContext context) {
80 if (context != null && context.getUserPrincipal() != null) {
81 username = context.getUserPrincipal().getName();
85 protected String getUserName() {
89 private void handleContainerDoesNotExist(String containerName) {
90 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
91 IContainerManager.class, this);
92 if (containerManager == null) {
93 throw new ServiceUnavailableException("Container " + RestMessages.NOCONTAINER.toString());
96 List<String> containerNames = containerManager.getContainerNames();
97 for (String cName : containerNames) {
98 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
103 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
106 private void handleNameMismatch(String name, String nameinURL) {
107 if (name == null || nameinURL == null) {
108 throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
111 if (name.equals(nameinURL)) {
114 throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
115 + " : Name in URL does not match the name in request body");
119 * List all the subnets in a given container
121 * @param containerName
122 * container in which we want to query the subnets
124 * @return a List of SubnetConfig
130 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
132 * Response body in XML:
134 * <subnetConfig>
135 * <name>marketingdepartment</name>
136 * <subnet>30.31.54.254/24</subnet>
137 * </subnetConfig>
138 * <subnetConfig>
139 * <name>salesdepartment</name>
140 * <subnet>20.18.1.254/16</subnet>
141 * <nodeConnectors>OF|11@OF|00:00:00:aa:bb:cc:dd:ee</nodeConnectors>
142 * <nodeConnectors>OF|13@OF|00:00:00:aa:bb:cc:dd:ee</nodeConnectors>
143 * </subnetConfig>
145 * Response body in JSON:
149 * "name": "marketingdepartment",
150 * "subnet": "30.31.54.254/24",
151 * "nodeConnectors": [
152 * "OF|04@OF|00:00:00:00:00:00:00:04",
153 * "OF|07@OF|00:00:00:00:00:00:00:07"
157 * "name":"salesdepartment",
158 * "subnet":"20.18.1.254/16",
159 * "nodeConnectors": [
160 * "OF|11@OF|00:00:00:aa:bb:cc:dd:ee",
161 * "OF|13@OF|00:00:00:aa:bb:cc:dd:ee"
169 @Path("/{containerName}/subnets")
171 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
172 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
173 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
174 @ResponseCode(code = 503, condition = "Service unavailable"),
175 @ResponseCode(code = 400, condition = "Incorrect query syntex") })
176 @TypeHint(SubnetConfigs.class)
177 public SubnetConfigs listSubnets(@PathParam("containerName") String containerName,
178 @QueryParam("_q") String queryString) {
180 handleContainerDoesNotExist(containerName);
181 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
182 throw new UnauthorizedException("User is not authorized to perform this operation on container "
185 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
187 if (switchManager == null) {
188 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
190 List<SubnetConfig> subnets = switchManager.getSubnetsConfigList();
191 if (queryString != null) {
192 subnets = queryContext.createQuery(queryString, SubnetConfig.class)
196 return new SubnetConfigs(subnets);
200 * List the configuration of a subnet in a given container
202 * @param containerName
203 * container in which we want to query the subnet
205 * of the subnet being queried
207 * @return SubnetConfig
213 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/marketingdepartment
215 * Response body in XML:
216 * <subnetConfig>
217 * <name>marketingdepartment</name>
218 * <subnet>30.0.0.1/24</subnet>
219 * <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodePorts>
220 * <nodeConnectors>OF|3@OF|00:00:00:00:00:00:00:03</nodePorts>
221 * </subnetConfig>
223 * Response body in JSON:
225 * "name":"marketingdepartment",
226 * "subnet":"30.0.0.1/24",
228 * "OF|1@OF|00:00:00:00:00:00:00:01",
229 * "OF|3@OF|00:00:00:00:00:00:00:03"
234 @Path("/{containerName}/subnet/{subnetName}")
236 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
237 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
238 @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
239 @ResponseCode(code = 503, condition = "Service unavailable") })
240 @TypeHint(SubnetConfig.class)
241 public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
242 @PathParam("subnetName") String subnetName) {
244 handleContainerDoesNotExist(containerName);
246 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
247 throw new UnauthorizedException("User is not authorized to perform this operation on container "
250 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
252 if (switchManager == null) {
253 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
255 SubnetConfig res = switchManager.getSubnetConfig(subnetName);
257 throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
263 * Add a subnet into the specified container context, node connectors are
266 * @param containerName
267 * name of the container context in which the subnet needs to be
270 * name of new subnet to be added
271 * @param subnetConfigData
272 * the {@link SubnetConfig} structure in request body
274 * @return Response as dictated by the HTTP Response Status code
280 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
282 * Request body in XML:
283 * <subnetConfig>
284 * <name>salesdepartment</name>
285 * <subnet>172.173.174.254/24</subnet>
286 * <nodeConnectors>OF|22@OF|00:00:11:22:33:44:55:66</nodeConnectors>
287 * <nodeConnectors>OF|39@OF|00:00:ab:cd:33:44:55:66</nodeConnectors>
288 * </subnetConfig>
290 * Request body in JSON:
292 * "name":"salesdepartment",
293 * "subnet":"172.173.174.254/24",
295 * "OF|22@OF|00:00:11:22:33:44:55:66",
296 * "OF|39@OF|00:00:ab:cd:33:44:55:66"
302 @Path("/{containerName}/subnet/{subnetName}")
304 @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
305 @ResponseCode(code = 400, condition = "Invalid data passed"),
306 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
307 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
308 @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
309 @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
310 @ResponseCode(code = 503, condition = "Service unavailable") })
311 public Response addSubnet(@PathParam("containerName") String containerName,
312 @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
314 handleContainerDoesNotExist(containerName);
316 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
317 throw new UnauthorizedException("User is not authorized to perform this operation on container "
320 SubnetConfig cfgObject = subnetConfigData;
321 handleNameMismatch(cfgObject.getName(), subnetName);
323 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
325 if (switchManager == null) {
326 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
328 Status status = switchManager.addSubnet(cfgObject);
329 if (status.isSuccess()) {
330 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
331 if (subnetConfigData.getNodeConnectors() != null) {
332 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
333 NorthboundUtils.auditlog("Port", getUserName(), "added",
334 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway " + subnetName,
338 return Response.status(Response.Status.CREATED).build();
340 return NorthboundUtils.getResponse(status);
344 * Delete a subnet from the specified container context
346 * @param containerName
347 * name of the container in which subnet needs to be removed
349 * name of new subnet to be deleted
350 * @return Response as dictated by the HTTP Response Status code
356 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/engdepartment
360 @Path("/{containerName}/subnet/{subnetName}")
362 @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
363 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
364 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
365 @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
366 @ResponseCode(code = 503, condition = "Service unavailable") })
367 public Response removeSubnet(@PathParam("containerName") String containerName,
368 @PathParam("subnetName") String subnetName) {
370 handleContainerDoesNotExist(containerName);
372 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
373 throw new UnauthorizedException("User is not authorized to perform this operation on container "
377 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
379 if (switchManager == null) {
380 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
382 Status status = switchManager.removeSubnet(subnetName);
383 if (status.isSuccess()) {
384 NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
385 return Response.status(Response.Status.NO_CONTENT).build();
387 return NorthboundUtils.getResponse(status);
391 * Modify a subnet. Replace the existing subnet with the new specified one.
392 * For now only port list modification is allowed. If the respective subnet
393 * configuration does not exist this call is equivalent to a subnet
396 * @param containerName
397 * Name of the Container context
399 * Name of the subnet to be modified
400 * @param subnetConfigData
401 * the {@link SubnetConfig} structure in request body parameter
402 * @return Response as dictated by the HTTP Response Status code
408 * http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/salesdepartment
410 * Request body in XML:
411 * <subnetConfig>
412 * <name>salesdepartment</name>
413 * <subnet>172.173.174.254/24</subnet>
414 * <nodeConnectors>OF|22@OF|00:00:11:22:33:44:55:66</nodeConnectors>
415 * <nodeConnectors>OF|39@OF|00:00:ab:cd:33:44:55:66</nodeConnectors>
416 * </subnetConfig>
418 * Request body in JSON:
420 * "name":"salesdepartment",
421 * "subnet":"172.173.174.254/24",
423 * "OF|22@OF|00:00:11:22:33:44:55:66",
424 * "OF|39@OF|00:00:ab:cd:33:44:55:66"
429 @Path("/{containerName}/subnet/{subnetName}")
431 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
432 @StatusCodes({ @ResponseCode(code = 200, condition = "Configuration replaced successfully"),
433 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
434 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
435 @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
436 @ResponseCode(code = 500, condition = "Internal server error: Modify subnet failed"),
437 @ResponseCode(code = 503, condition = "Service unavailable") })
438 public Response modifySubnet(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
439 @PathParam("subnetName") String subnetName, @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
441 handleContainerDoesNotExist(containerName);
443 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
444 throw new UnauthorizedException("User is not authorized to perform this operation on container "
447 handleNameMismatch(subnetConfigData.getName(), subnetName);
449 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
451 if (switchManager == null) {
452 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
455 // Need to check this until Status does not return a CREATED status code
456 SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
458 Status status = switchManager.modifySubnet(subnetConfigData);
460 if (status.isSuccess()) {
461 if (existingConf == null) {
462 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
463 if (subnetConfigData.getNodeConnectors() != null) {
464 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
465 NorthboundUtils.auditlog("Port", getUserName(), "added",
466 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway" + subnetName,
470 return Response.created(uriInfo.getRequestUri()).build();
472 Set<NodeConnector> existingNCList = existingConf.getNodeConnectors();
474 if (existingNCList == null) {
475 existingNCList = new HashSet<NodeConnector>(0);
477 if (subnetConfigData.getNodeConnectors() != null) {
478 for (NodeConnector port : subnetConfigData.getNodeConnectors()) {
479 if (!existingNCList.contains(port)) {
480 NorthboundUtils.auditlog("Port", getUserName(), "added",
481 NorthboundUtils.getPortName(port, switchManager) + " to Subnet Gateway "
482 + subnetName, containerName);
486 for (NodeConnector port : existingNCList) {
487 if (!subnetConfigData.getNodeConnectors().contains(port)) {
489 .auditlog("Port", getUserName(), "removed",
490 NorthboundUtils.getPortName(port, switchManager) + " from Subnet Gateway "
491 + subnetName, containerName);
496 return NorthboundUtils.getResponse(status);