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
9 package org.opendaylight.controller.flowprogrammer.northbound;
11 import java.util.ArrayList;
12 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.forwardingrulesmanager.FlowConfig;
33 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
34 import org.opendaylight.controller.northbound.commons.RestMessages;
35 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
36 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
37 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
38 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
39 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
40 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
41 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
42 import org.opendaylight.controller.sal.authorization.Privilege;
43 import org.opendaylight.controller.sal.core.Node;
44 import org.opendaylight.controller.sal.utils.GlobalConstants;
45 import org.opendaylight.controller.sal.utils.ServiceHelper;
46 import org.opendaylight.controller.sal.utils.Status;
47 import org.opendaylight.controller.switchmanager.ISwitchManager;
50 * Flow Configuration Northbound API
54 * Authentication scheme : <b>HTTP Basic</b><br>
55 * Authentication realm : <b>opendaylight</b><br>
56 * Transport : <b>HTTP and HTTPS</b><br>
58 * HTTPS Authentication is disabled by default. Administrator can enable it in
59 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
60 * trusted authority.<br>
62 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
66 public class FlowProgrammerNorthbound {
68 private String username;
71 public void setSecurityContext(SecurityContext context) {
72 username = context.getUserPrincipal().getName();
75 protected String getUserName() {
79 private IForwardingRulesManager getForwardingRulesManagerService(
80 String containerName) {
81 IContainerManager containerManager = (IContainerManager) ServiceHelper
82 .getGlobalInstance(IContainerManager.class, this);
83 if (containerManager == null) {
84 throw new ServiceUnavailableException("Container "
85 + RestMessages.SERVICEUNAVAILABLE.toString());
88 boolean found = false;
89 List<String> containerNames = containerManager.getContainerNames();
90 for (String cName : containerNames) {
91 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
97 throw new ResourceNotFoundException(containerName + " "
98 + RestMessages.NOCONTAINER.toString());
101 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
102 .getInstance(IForwardingRulesManager.class, containerName, this);
105 throw new ServiceUnavailableException("Flow Programmer "
106 + RestMessages.SERVICEUNAVAILABLE.toString());
112 private List<FlowConfig> getStaticFlowsInternal(String containerName,
114 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
117 throw new ServiceUnavailableException("Flow Programmer "
118 + RestMessages.SERVICEUNAVAILABLE.toString());
121 List<FlowConfig> flows = new ArrayList<FlowConfig>();
124 for (FlowConfig flow : frm.getStaticFlows()) {
128 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
129 ISwitchManager.class, containerName, this);
132 throw new ServiceUnavailableException("Switch Manager "
133 + RestMessages.SERVICEUNAVAILABLE.toString());
136 if (!sm.getNodes().contains(node)) {
137 throw new ResourceNotFoundException(node.toString() + " : "
138 + RestMessages.NONODE.toString());
141 for (FlowConfig flow : frm.getStaticFlows(node)) {
149 * Returns a list of Flows configured on the given container
151 * @param containerName
152 * Name of the Container. The Container name for the base
153 * controller is "default".
154 * @return List of configured flows configured on a given container
156 @Path("/{containerName}")
158 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
159 @TypeHint(FlowConfigs.class)
161 @ResponseCode(code = 200, condition = "Operation successful"),
162 @ResponseCode(code = 404, condition = "The containerName is not found"),
163 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
164 public FlowConfigs getStaticFlows(
165 @PathParam("containerName") String containerName) {
166 if (!NorthboundUtils.isAuthorized(
167 getUserName(), containerName, Privilege.READ, this)) {
168 throw new UnauthorizedException(
169 "User is not authorized to perform this operation on container "
173 List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName,
175 return new FlowConfigs(flowConfigs);
179 * Returns a list of Flows configured on a Node in a given container
181 * @param containerName
182 * Name of the Container. The Container name for the base
183 * controller is "default".
185 * Type of the node being programmed
188 * @return List of configured flows configured on a Node in a container
190 @Path("/{containerName}/{nodeType}/{nodeId}")
192 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
193 @TypeHint(FlowConfigs.class)
195 @ResponseCode(code = 200, condition = "Operation successful"),
196 @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
197 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
198 public FlowConfigs getStaticFlows(
199 @PathParam("containerName") String containerName,
200 @PathParam("nodeType") String nodeType,
201 @PathParam("nodeId") String nodeId) {
202 if (!NorthboundUtils.isAuthorized(
203 getUserName(), containerName, Privilege.READ, this)) {
204 throw new UnauthorizedException(
205 "User is not authorized to perform this operation on container "
208 Node node = Node.fromString(nodeType, nodeId);
210 throw new ResourceNotFoundException(nodeId + " : "
211 + RestMessages.NONODE.toString());
213 List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
214 return new FlowConfigs(flows);
218 * Returns the flow configuration matching a human-readable name and nodeId
219 * on a given Container.
221 * @param containerName
222 * Name of the Container. The Container name for the base
223 * controller is "default".
225 * Type of the node being programmed
229 * Human-readable name for the configured flow.
230 * @return Flow configuration matching the name and nodeId on a Container
232 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
234 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
235 @TypeHint(FlowConfig.class)
237 @ResponseCode(code = 200, condition = "Operation successful"),
238 @ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
239 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
240 public FlowConfig getStaticFlow(
241 @PathParam("containerName") String containerName,
242 @PathParam("nodeType") String nodeType,
243 @PathParam("nodeId") String nodeId, @PathParam("name") String name) {
244 if (!NorthboundUtils.isAuthorized(
245 getUserName(), containerName, Privilege.READ, this)) {
246 throw new UnauthorizedException(
247 "User is not authorized to perform this operation on container "
250 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
253 throw new ServiceUnavailableException("Flow Programmer "
254 + RestMessages.SERVICEUNAVAILABLE.toString());
257 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
259 FlowConfig staticFlow = frm.getStaticFlow(name, node);
260 if (staticFlow == null) {
261 throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
264 return new FlowConfig(staticFlow);
268 * Add a flow configuration
270 * @param containerName
271 * Name of the Container. The Container name for the base
272 * controller is "default".
274 * Type of the node being programmed
278 * Name of the Static Flow configuration
280 * Flow Configuration in JSON or XML format
281 * @return Response as dictated by the HTTP Response Status code
284 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
286 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
288 @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
289 @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
290 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
291 @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name"),
292 @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
293 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
294 public Response addFlow(
295 @PathParam(value = "containerName") String containerName,
296 @PathParam(value = "name") String name,
297 @PathParam("nodeType") String nodeType,
298 @PathParam(value = "nodeId") String nodeId,
299 @TypeHint(FlowConfig.class) JAXBElement<FlowConfig> flowConfig) {
301 if (!NorthboundUtils.isAuthorized(
302 getUserName(), containerName, Privilege.WRITE, this)) {
303 throw new UnauthorizedException(
304 "User is not authorized to perform this operation on container "
307 handleDefaultDisabled(containerName);
309 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
312 throw new ServiceUnavailableException("Flow Programmer "
313 + RestMessages.SERVICEUNAVAILABLE.toString());
316 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
318 FlowConfig staticFlow = frm.getStaticFlow(name, node);
319 if (staticFlow != null) {
320 throw new ResourceConflictException(name + " already exists."
321 + RestMessages.RESOURCECONFLICT.toString());
324 Status status = frm.addStaticFlow(flowConfig.getValue(), false);
325 if (status.isSuccess()) {
326 return Response.status(Response.Status.CREATED).build();
328 throw new InternalServerErrorException(status.getDescription());
332 * Delete a Flow configuration
334 * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
336 * @param containerName
337 * Name of the Container. The Container name for the base
338 * controller is "default".
340 * Type of the node being programmed
344 * Name of the Static Flow configuration
345 * @return Response as dictated by the HTTP Response code
348 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
350 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
352 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
353 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
354 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
355 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
356 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
357 public Response deleteFlow(
358 @PathParam(value = "containerName") String containerName,
359 @PathParam(value = "name") String name,
360 @PathParam("nodeType") String nodeType,
361 @PathParam(value = "nodeId") String nodeId) {
363 if (!NorthboundUtils.isAuthorized(
364 getUserName(), containerName, Privilege.WRITE, this)) {
365 throw new UnauthorizedException(
366 "User is not authorized to perform this operation on container "
369 handleDefaultDisabled(containerName);
371 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
374 throw new ServiceUnavailableException("Flow Programmer "
375 + RestMessages.SERVICEUNAVAILABLE.toString());
378 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
380 FlowConfig staticFlow = frm.getStaticFlow(name, node);
381 if (staticFlow == null) {
382 throw new ResourceNotFoundException(name + " : "
383 + RestMessages.NOFLOW.toString());
386 Status status = frm.removeStaticFlow(name, node);
387 if (status.isSuccess()) {
388 return Response.ok().build();
390 throw new InternalServerErrorException(status.getDescription());
394 * Toggle a Flow configuration
396 * @param containerName
397 * Name of the Container. The Container name for the base
398 * controller is "default".
400 * Type of the node being programmed
404 * Name of the Static Flow configuration
405 * @return Response as dictated by the HTTP Response code
408 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
410 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
412 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
413 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
414 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
415 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
416 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
417 public Response toggleFlow(
418 @PathParam(value = "containerName") String containerName,
419 @PathParam("nodeType") String nodeType,
420 @PathParam(value = "nodeId") String nodeId,
421 @PathParam(value = "name") String name) {
423 if (!NorthboundUtils.isAuthorized(
424 getUserName(), containerName, Privilege.WRITE, this)) {
425 throw new UnauthorizedException(
426 "User is not authorized to perform this operation on container "
430 handleDefaultDisabled(containerName);
432 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
435 throw new ServiceUnavailableException("Flow Programmer "
436 + RestMessages.SERVICEUNAVAILABLE.toString());
439 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
441 FlowConfig staticFlow = frm.getStaticFlow(name, node);
442 if (staticFlow == null) {
443 throw new ResourceNotFoundException(name + " : "
444 + RestMessages.NOFLOW.toString());
447 Status status = frm.toggleStaticFlowStatus(staticFlow);
448 if (status.isSuccess()) {
449 return Response.ok().build();
451 throw new InternalServerErrorException(status.getDescription());
454 private Node handleNodeAvailability(String containerName, String nodeType,
457 Node node = Node.fromString(nodeType, nodeId);
459 throw new ResourceNotFoundException(nodeId + " : "
460 + RestMessages.NONODE.toString());
463 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
464 ISwitchManager.class, containerName, this);
467 throw new ServiceUnavailableException("Switch Manager "
468 + RestMessages.SERVICEUNAVAILABLE.toString());
471 if (!sm.getNodes().contains(node)) {
472 throw new ResourceNotFoundException(node.toString() + " : "
473 + RestMessages.NONODE.toString());
478 private void handleDefaultDisabled(String containerName) {
479 IContainerManager containerManager = (IContainerManager) ServiceHelper
480 .getGlobalInstance(IContainerManager.class, this);
481 if (containerManager == null) {
482 throw new InternalServerErrorException(
483 RestMessages.INTERNALERROR.toString());
485 if (containerName.equals(GlobalConstants.DEFAULT.toString())
486 && containerManager.hasNonDefaultContainer()) {
487 throw new NotAcceptableException(
488 RestMessages.DEFAULTDISABLED.toString());