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.xml.bind.JAXBElement;
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.InternalServerErrorException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
36 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
37 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
38 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
39 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
40 import org.opendaylight.controller.sal.authorization.Privilege;
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 public class SubnetsNorthbound {
50 protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
52 private String username;
55 public void setSecurityContext(SecurityContext context) {
56 if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
59 protected String getUserName() {
63 private void handleContainerDoesNotExist(String containerName) {
64 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
65 IContainerManager.class, this);
66 if (containerManager == null) {
67 throw new ServiceUnavailableException("Container " + RestMessages.NOCONTAINER.toString());
70 List<String> containerNames = containerManager.getContainerNames();
71 for (String cName : containerNames) {
72 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
77 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
80 private void handleNameMismatch(String name, String nameinURL) {
81 if (name == null || nameinURL == null) {
82 throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
85 if (name.equals(nameinURL)) {
88 throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
89 + " : Name in URL does not match the name in request body");
93 * List all the subnets in a given container
95 * @param containerName
96 * container in which we want to query the subnets
98 * @return a List of SubnetConfig
103 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/all
106 * <subnetConfig>
107 * <name>subnet1</name>
108 * <subnet>30.0.0.1/24</subnet>
109 * <nodePorts>1/1>/nodePorts>
110 * <nodePorts>1/2>/nodePorts>
111 * <nodePorts>1/3>/nodePorts>
112 * </subnetConfig>
113 * <subnetConfig>
114 * <name>subnet2</name>
115 * <subnet>20.0.0.1/24</subnet>
116 * <nodePorts>2/1>/nodePorts>
117 * <nodePorts>2/2>/nodePorts>
118 * <nodePorts>2/3>/nodePorts>
119 * </subnetConfig>
124 * "subnet":"30.0.0.1/24",
125 * "nodePorts":["1/1","1/2","1/3"]
129 * "subnet":"20.0.0.1/24",
130 * "nodePorts":["2/1","2/2","2/3"]
134 @Path("/{containerName}/subnet/all")
136 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
137 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
138 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
139 @ResponseCode(code = 503, condition = "Service unavailable") })
140 @TypeHint(SubnetConfigs.class)
141 public SubnetConfigs listSubnets(@PathParam("containerName") String containerName) {
143 handleContainerDoesNotExist(containerName);
144 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
145 throw new UnauthorizedException("User is not authorized to perform this operation on container "
148 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
149 if (switchManager == null) {
150 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
152 return new SubnetConfigs(switchManager.getSubnetsConfigList());
156 * List the configuration of a subnet in a given container
158 * @param containerName
159 * container in which we want to query the subnet
161 * of the subnet being queried
163 * @return SubnetConfig
168 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
171 * <subnetConfig>
172 * <name>subnet1</name>
173 * <subnet>30.0.0.1/24</subnet>
174 * <nodePorts>1/1>/nodePorts>
175 * <nodePorts>1/2>/nodePorts>
176 * <nodePorts>1/3>/nodePorts>
177 * </subnetConfig>
182 * "subnet":"30.0.0.1/24",
183 * "nodePorts":["1/1","1/2","1/3"]
187 @Path("/{containerName}/subnet/{subnetName}")
189 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
190 @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
191 @ResponseCode(code = 404, condition = "The containerName or subnetName passed was not found"),
192 @ResponseCode(code = 503, condition = "Service unavailable") })
193 @TypeHint(SubnetConfig.class)
194 public SubnetConfig listSubnet(@PathParam("containerName") String containerName,
195 @PathParam("subnetName") String subnetName) {
197 handleContainerDoesNotExist(containerName);
199 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
200 throw new UnauthorizedException("User is not authorized to perform this operation on container "
203 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
204 if (switchManager == null) {
205 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
207 SubnetConfig res = switchManager.getSubnetConfig(subnetName);
209 throw new ResourceNotFoundException(RestMessages.NOSUBNET.toString());
216 * Add a subnet to a container
218 * @param containerName
219 * name of the container to which subnet needs to be added
221 * name of new subnet to be added
222 * @param subnetConfigData
223 * the {@link SubnetConfig} structure in request body
225 * @return Response as dictated by the HTTP Response Status code
230 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
233 * <subnetConfig>
234 * <name>subnet1</name>
235 * <subnet>30.0.0.1/24</subnet>
236 * </subnetConfig>
241 * "subnet":"30.0.0.1/24"
246 @Path("/{containerName}/subnet/{subnetName}")
248 @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
249 @ResponseCode(code = 400, condition = "Invalid data passed"),
250 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
251 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
252 @ResponseCode(code = 404, condition = "Container name passed was not found or subnet config is null"),
253 @ResponseCode(code = 500, condition = "Internal Server Error: Addition of subnet failed"),
254 @ResponseCode(code = 503, condition = "Service unavailable") })
255 public Response addSubnet(@PathParam("containerName") String containerName,
256 @PathParam("subnetName") String subnetName,
257 @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
259 handleContainerDoesNotExist(containerName);
261 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
262 throw new UnauthorizedException("User is not authorized to perform this operation on container "
265 SubnetConfig cfgObject = subnetConfigData;
266 handleNameMismatch(cfgObject.getName(), subnetName);
268 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
269 if (switchManager == null) {
270 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
272 Set<String> ports = cfgObject.getNodePorts();
273 SubnetConfig subnetCfg = null;
275 subnetCfg = new SubnetConfig(cfgObject.getName(), cfgObject.getSubnet(), new HashSet<String>(0));
277 subnetCfg = cfgObject;
280 Status status = switchManager.addSubnet(subnetCfg);
282 if (status.isSuccess()) {
283 NorthboundUtils.auditlog("Subnet Gateway", username, "added", subnetName, containerName);
284 return Response.status(Response.Status.CREATED).build();
286 return NorthboundUtils.getResponse(status);
290 * Delete a subnet from a container
292 * @param containerName
293 * name of the container from which subnet needs to be removed
295 * name of new subnet to be deleted
296 * @return Response as dictated by the HTTP Response Status code
300 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
304 @Path("/{containerName}/subnet/{subnetName}")
306 @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"),
307 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
308 @ResponseCode(code = 404, condition = "The containerName passed was not found"),
309 @ResponseCode(code = 500, condition = "Internal Server Error : Removal of subnet failed"),
310 @ResponseCode(code = 503, condition = "Service unavailable") })
311 public Response removeSubnet(@PathParam("containerName") String containerName,
312 @PathParam("subnetName") String subnetName) {
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 "
321 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
322 if (switchManager == null) {
323 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
325 Status status = switchManager.removeSubnet(subnetName);
326 if (status.isSuccess()) {
327 NorthboundUtils.auditlog("Subnet Gateway", username, "removed", subnetName, containerName);
328 return Response.status(Response.Status.NO_CONTENT).build();
330 return NorthboundUtils.getResponse(status);
334 * Modify a subnet. For now only changing the port list is allowed.
336 * @param containerName
337 * Name of the Container
339 * Name of the SubnetConfig to be modified
340 * @param subnetConfigData
341 * the {@link SubnetConfig} structure in request body
343 * @return If the operation is successful or not
348 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
351 * <subnetConfig>
352 * <name>subnet1</name>
353 * <subnet>30.0.0.1/24</subnet>
354 * <nodePorts>1/1</nodePorts>
355 * <nodePorts>1/2</nodePorts>
356 * <nodePorts>1/3</nodePorts>
357 * </subnetConfig>
362 * "subnet":"30.0.0.1/24",
363 * "nodePorts":["1/1","1/2","1/3"]
367 @Path("/{containerName}/subnet/{subnetName}/node-ports")
369 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
371 @ResponseCode(code = 200, condition = "Ports replaced successfully"),
372 @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
373 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
374 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
375 @ResponseCode(code = 404, condition = "The containerName or subnetName is not found"),
376 @ResponseCode(code = 500, condition = "Internal server error: Modify ports failed"),
377 @ResponseCode(code = 503, condition = "Service unavailable") })
378 public Response modifySubnet(@PathParam("containerName") String containerName,
379 @PathParam("subnetName") String subnetName,
380 @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
382 handleContainerDoesNotExist(containerName);
384 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
385 throw new UnauthorizedException("User is not authorized to perform this operation on container "
388 handleNameMismatch(subnetConfigData.getName(), subnetName);
390 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
392 if (switchManager == null) {
393 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
396 SubnetConfig subnetConf = subnetConfigData;
397 SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
399 boolean successful = true;
401 // make sure that the name matches an existing subnet and we're not
402 // changing the name or subnet IP/mask
403 if (existingConf == null) {
404 // don't have a subnet by that name
405 return Response.status(Response.Status.NOT_FOUND).build();
407 } else if (!existingConf.getName().equals(subnetConf.getName())
408 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
409 // can't change the name of a subnet
410 Response.status(Response.Status.BAD_REQUEST).build();
412 // create a set for fast lookups
413 Set<String> newPorts = new HashSet<String>(subnetConf.getNodePorts());
415 // go through the current ports and (1) remove ports that aren't
416 // there anymore and (2) remove ports that are still there from the
417 // set of ports to add
418 for (String s : existingConf.getNodePorts()) {
419 if (newPorts.contains(s)) {
422 Status st = switchManager.removePortsFromSubnet(subnetName, s);
423 successful = successful && st.isSuccess();
427 // add any remaining ports
428 for (String s : newPorts) {
429 Status st = switchManager.addPortsToSubnet(subnetName, s);
430 successful = successful && st.isSuccess();
432 NorthboundUtils.auditlog("Subnet Gateway", username, "added", s + " to " + subnetName,
439 return Response.status(Response.Status.OK).build();
441 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
445 * Add ports to a subnet in the container
447 * @param containerName
448 * name of the container that has the subnet to which node ports
451 * name of subnet to which node ports need to be added
452 * @param SubnetConfig
453 * the {@link SubnetConfig} structure in request body
454 * @return Response as dictated by the HTTP Response Status code
458 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
461 * <subnetConfig>
462 * <name>subnet1</name>
463 * <subnet>30.0.0.1/24</subnet>
464 * <nodePorts>1/1</nodePorts>
465 * <nodePorts>1/2</nodePorts>
466 * <nodePorts>1/3</nodePorts>
467 * </subnetConfig>
472 * "subnet":"30.0.0.1/24",
473 * "nodePorts":["1/1","1/2","1/3"]
478 @Path("/{containerName}/subnet/{subnetName}/node-ports")
480 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
482 @ResponseCode(code = 200, condition = "Added node ports to subnet successfully"),
483 @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
484 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
485 @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
486 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
487 @ResponseCode(code = 500, condition = "Internal server error : Port add failed"),
488 @ResponseCode(code = 503, condition = "Service unavailable") })
489 public Response addNodePorts(@PathParam("containerName") String containerName,
490 @PathParam("subnetName") String subnetName,
491 @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
493 handleContainerDoesNotExist(containerName);
495 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
496 throw new UnauthorizedException("User is not authorized to perform this operation on container "
499 handleNameMismatch(subnetConfigData.getName(), subnetName);
501 SubnetConfig subnetConf = subnetConfigData;
503 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
505 if (switchManager == null) {
506 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
509 SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
511 // make sure that the name matches an existing subnet and we're not
512 // changing the name or subnet IP/mask
513 if (existingConf == null) {
514 // don't have a subnet by that name
515 return Response.status(Response.Status.NOT_FOUND).build();
516 } else if (!existingConf.getName().equals(subnetConf.getName())
517 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
518 // can't change the name of a subnet
519 return Response.status(Response.Status.BAD_REQUEST).build();
522 boolean successful = true;
523 Set<String> ports = subnetConf.getNodePorts();
525 if (ports == null || ports.isEmpty()) {
526 throw new BadRequestException(RestMessages.INVALIDDATA.toString());
529 // add new ports only
530 if (existingConf.getNodePorts() != null) {
531 ports.removeAll(existingConf.getNodePorts());
533 for (String port : ports) {
534 st = switchManager.addPortsToSubnet(subnetName, port);
535 successful = successful && st.isSuccess();
537 NorthboundUtils.auditlog("Subnet Gateway", username, "added", st + " to " + subnetName, containerName);
541 return Response.status(Response.Status.OK).build();
543 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
547 * Delete ports from a subnet in the container
549 * @param containerName
550 * name of the container that has the subnet from which node
551 * ports need to be deleted
553 * name of subnet from which node ports need to be deleted
554 * @param subnetConfigData
555 * SubnetConfig object to be deleted
556 * @return Response as dictated by the HTTP Response Status code
560 * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
563 * <subnetConfig>
564 * <name>subnet3</name>
565 * <subnet>30.0.0.1/24</subnet>
566 * <nodePorts>1/1,1/2,1/3</nodePorts>
567 * </subnetConfig>
570 * { "name" : "subnet1",
571 * "subnet" : "30.0.0.1/24",
572 * nodePorts : ["1/1,1/2,1/3"]}
576 @Path("/{containerName}/subnet/{subnetName}/node-ports")
578 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
580 @ResponseCode(code = 204, condition = "No content"),
581 @ResponseCode(code = 400, condition = "Invalid request to change subnet name or invalid node ports passed"),
582 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
583 @ResponseCode(code = 404, condition = "The containerName or subnet is not found"),
584 @ResponseCode(code = 409, condition = "Subnet name in url conflicts with name in request body"),
585 @ResponseCode(code = 500, condition = "Internal server error : Delete node ports failed"),
586 @ResponseCode(code = 503, condition = "Service unavailable") })
587 public Response deleteNodePorts(@PathParam("containerName") String containerName,
588 @PathParam("subnetName") String subnetName,
589 @TypeHint(SubnetConfig.class) SubnetConfig subnetConfigData) {
591 handleContainerDoesNotExist(containerName);
593 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
594 throw new UnauthorizedException("User is not authorized to perform this operation on container "
597 handleNameMismatch(subnetConfigData.getName(), subnetName);
599 SubnetConfig subnetConf = subnetConfigData;
601 if (subnetConf.getNodePorts() == null || subnetConf.getNodePorts().isEmpty()) {
602 throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : invalid node ports");
605 ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
607 if (switchManager == null) {
608 throw new ServiceUnavailableException("SwitchManager " + RestMessages.SERVICEUNAVAILABLE.toString());
611 SubnetConfig existingConf = switchManager.getSubnetConfig(subnetName);
613 // make sure that the name matches an existing subnet and we're not
614 // changing the name or subnet IP/mask
615 if (existingConf == null) {
616 // don't have a subnet by that name
617 return Response.status(Response.Status.NOT_FOUND).build();
618 } else if (!existingConf.getName().equals(subnetConf.getName())
619 || !existingConf.getSubnet().equals(subnetConf.getSubnet())) {
620 // can't change the name of a subnet
621 return Response.status(Response.Status.BAD_REQUEST).build();
624 boolean successful = true;
625 Set<String> ports = subnetConf.getNodePorts();
627 // delete existing ports
628 ports.retainAll(existingConf.getNodePorts());
629 for (String port : ports) {
630 st = switchManager.removePortsFromSubnet(subnetName, port);
631 successful = successful && st.isSuccess();
633 NorthboundUtils.auditlog("Subnet Gateway", username, "removed", st + " from " + subnetName,
638 return Response.status(Response.Status.NO_CONTENT).build();
640 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());