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;
27 import org.codehaus.enunciate.jaxrs.ResponseCode;
28 import org.codehaus.enunciate.jaxrs.StatusCodes;
29 import org.codehaus.enunciate.jaxrs.TypeHint;
30 import org.opendaylight.controller.containermanager.IContainerManager;
31 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
32 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
33 import org.opendaylight.controller.northbound.commons.RestMessages;
34 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
35 import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException;
36 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
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.utils.NorthboundUtils;
41 import org.opendaylight.controller.sal.authorization.Privilege;
42 import org.opendaylight.controller.sal.core.Node;
43 import org.opendaylight.controller.sal.utils.GlobalConstants;
44 import org.opendaylight.controller.sal.utils.ServiceHelper;
45 import org.opendaylight.controller.sal.utils.Status;
46 import org.opendaylight.controller.switchmanager.ISwitchManager;
49 * Flow Configuration Northbound API provides capabilities to program flows.
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.
61 public class FlowProgrammerNorthbound {
63 private String username;
66 public void setSecurityContext(SecurityContext context) {
67 if (context != null && context.getUserPrincipal() != null) {
68 username = context.getUserPrincipal().getName();
72 protected String getUserName() {
76 private IForwardingRulesManager getForwardingRulesManagerService(String containerName) {
77 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
78 IContainerManager.class, this);
79 if (containerManager == null) {
80 throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString());
83 boolean found = false;
84 List<String> containerNames = containerManager.getContainerNames();
85 for (String cName : containerNames) {
86 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
92 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
95 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
96 IForwardingRulesManager.class, containerName, this);
99 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
105 private List<FlowConfig> getStaticFlowsInternal(String containerName, Node node) {
106 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
109 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
112 List<FlowConfig> flows = new ArrayList<FlowConfig>();
115 for (FlowConfig flow : frm.getStaticFlows()) {
119 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
122 throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString());
125 if (!sm.getNodes().contains(node)) {
126 throw new ResourceNotFoundException(node.toString() + " : " + RestMessages.NONODE.toString());
129 for (FlowConfig flow : frm.getStaticFlows(node)) {
137 * Returns a list of Flows configured on the given container
139 * @param containerName
140 * Name of the Container (Eg. 'default')
141 * @return List of flows configured on a given container
148 * http://localhost:8080/controller/nb/v2/flowprogrammer/default
150 * Response body in XML:
151 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
153 *    <flowConfig>
154 *       <installInHw>true</installInHw>
155 *       <name>flow1</name>
156 *       <node>
157 *          <id>00:00:00:00:00:00:00:01</id>
158 *          <type>OF</type>
159 *       </node>
160 *       <ingressPort>1</ingressPort>
161 *       <priority>500</priority>
162 *       <etherType>0x800</etherType>
163 *       <nwSrc>9.9.1.1</nwSrc>
164 *       <actions>OUTPUT=2</actions>
165 *    </flowConfig>
168 * Response body in JSON:
172 * "installInHw": "true",
176 * "id": "00:00:00:00:00:00:00:01"
178 * "ingressPort": "1",
180 * "etherType": "0x800",
190 @Path("/{containerName}")
192 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
193 @TypeHint(FlowConfigs.class)
194 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
195 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
196 @ResponseCode(code = 404, condition = "The containerName is not found"),
197 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
198 public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName) {
199 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
200 throw new UnauthorizedException("User is not authorized to perform this operation on container "
204 List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName, null);
205 return new FlowConfigs(flowConfigs);
209 * Returns a list of Flows configured on a Node in a given container
211 * @param containerName
212 * Name of the Container (Eg. 'default')
214 * Type of the node being programmed (Eg. 'OF')
216 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
217 * @return List of flows configured on a Node in a container
224 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
226 * Response body in XML:
227 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
229 *    <flowConfig>
230 *       <installInHw>true</installInHw>
231 *       <name>flow1</name>
232 *       <node>
233 *          <id>00:00:00:00:00:00:00:01</id>
234 *          <type>OF</type>
235 *       </node>
236 *       <ingressPort>1</ingressPort>
237 *       <priority>500</priority>
238 *       <etherType>0x800</etherType>
239 *       <nwSrc>9.9.1.1</nwSrc>
240 *       <actions>OUTPUT=2</actions>
241 *    </flowConfig>
244 * Response body in JSON:
248 * "installInHw": "true",
252 * "id": "00:00:00:00:00:00:00:01"
254 * "ingressPort": "1",
256 * "etherType": "0x800",
266 @Path("/{containerName}/node/{nodeType}/{nodeId}")
268 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
269 @TypeHint(FlowConfigs.class)
270 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
271 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
272 @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
273 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
274 public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName,
275 @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId) {
276 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
277 throw new UnauthorizedException("User is not authorized to perform this operation on container "
280 Node node = Node.fromString(nodeType, nodeId);
282 throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
284 List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
285 return new FlowConfigs(flows);
289 * Returns the flow configuration matching a human-readable name and nodeId
290 * on a given Container.
292 * @param containerName
293 * Name of the Container (Eg. 'default')
295 * Type of the node being programmed (Eg. 'OF')
297 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
299 * Human-readable name for the configured flow (Eg. 'Flow1')
300 * @return Flow configuration matching the name and nodeId on a Container
307 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
309 * Response body in XML:
310 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
312 *    <installInHw>true</installInHw>
313 *    <name>flow1</name>
314 *    <node>
315 *       <id>00:00:00:00:00:00:00:01</id>
316 *       <type>OF</type>
317 *    </node>
318 *    <ingressPort>1</ingressPort>
319 *    <priority>500</priority>
320 *    <etherType>0x800</etherType>
321 *    <nwSrc>9.9.1.1</nwSrc>
322 *    <actions>OUTPUT=2</actions>
323 * </flowConfig>
325 * Response body in JSON:
327 * "installInHw":"true",
330 * "id":"00:00:00:00:00:00:00:01",
335 * "etherType":"0x800",
344 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
346 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
347 @TypeHint(FlowConfig.class)
348 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
349 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
350 @ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
351 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
352 public FlowConfig getStaticFlow(@PathParam("containerName") String containerName,
353 @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId, @PathParam("name") String name) {
354 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
355 throw new UnauthorizedException("User is not authorized to perform this operation on container "
358 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
361 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
364 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
366 FlowConfig staticFlow = frm.getStaticFlow(name, node);
367 if (staticFlow == null) {
368 throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
371 return new FlowConfig(staticFlow);
375 * Add or Modify a flow configuration. If the flow exists already, it will replace the current flow.
377 * @param containerName
378 * Name of the Container (Eg. 'default')
380 * Type of the node being programmed (Eg. 'OF')
382 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
384 * Name of the Static Flow configuration (Eg. 'Flow2')
386 * Flow Configuration in JSON or XML format
387 * @return Response as dictated by the HTTP Response Status code
394 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
396 * Request body in XML:
398 *    <installInHw>true</installInHw>
399 *    <name>flow1</name>
400 *    <node>
401 *       <id>00:00:00:00:00:00:00:01</id>
402 *       <type>OF</type>
403 *    </node>
404 *    <ingressPort>1</ingressPort>
405 *    <priority>500</priority>
406 *    <etherType>0x800</etherType>
407 *    <nwSrc>9.9.1.1</nwSrc>
408 *    <actions>OUTPUT=2</actions>
409 * </flowConfig>
411 * Request body in JSON:
413 * "installInHw":"true",
416 * "id":"00:00:00:00:00:00:00:01",
421 * "etherType":"0x800",
430 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
432 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
434 @ResponseCode(code = 200, condition = "Static Flow modified successfully"),
435 @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
436 @ResponseCode(code = 400, condition = "Failed to create Static Flow entry due to invalid flow configuration"),
437 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
438 @ResponseCode(code = 404, condition = "The Container Name or nodeId is not found"),
439 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
440 @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name or configuration"),
441 @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
442 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
443 public Response addOrModifyFlow(@PathParam(value = "containerName") String containerName,
444 @PathParam(value = "name") String name, @PathParam("nodeType") String nodeType,
445 @PathParam(value = "nodeId") String nodeId, @TypeHint(FlowConfig.class) FlowConfig flowConfig) {
447 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
448 throw new UnauthorizedException("User is not authorized to perform this operation on container "
452 if (flowConfig.getNode() == null) {
453 return Response.status(Response.Status.BAD_REQUEST).entity("Invalid Configuration. Node is null or empty")
456 handleResourceCongruence(name, flowConfig.getName());
457 handleResourceCongruence(nodeId, flowConfig.getNode().getNodeIDString());
458 handleDefaultDisabled(containerName);
460 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
463 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
466 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
469 FlowConfig staticFlow = frm.getStaticFlow(name, node);
471 if (staticFlow == null) {
472 status = frm.addStaticFlow(flowConfig);
473 if(status.isSuccess()){
474 NorthboundUtils.auditlog("Flow Entry", username, "added",
475 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
476 return Response.status(Response.Status.CREATED).entity("Success").build();
479 status = frm.modifyStaticFlow(flowConfig);
480 if(status.isSuccess()){
481 NorthboundUtils.auditlog("Flow Entry", username, "updated",
482 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
483 return NorthboundUtils.getResponse(status);
486 return NorthboundUtils.getResponse(status);
490 * Delete a Flow configuration
492 * @param containerName
493 * Name of the Container (Eg. 'default')
495 * Type of the node being programmed (Eg. 'OF')
497 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
499 * Name of the Static Flow configuration (Eg. 'Flow1')
500 * @return Response as dictated by the HTTP Response code
507 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
512 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
515 @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
516 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
517 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
518 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
519 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
520 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
521 public Response deleteFlow(@PathParam(value = "containerName") String containerName,
522 @PathParam(value = "name") String name, @PathParam("nodeType") String nodeType,
523 @PathParam(value = "nodeId") String nodeId) {
525 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
526 throw new UnauthorizedException("User is not authorized to perform this operation on container "
529 handleDefaultDisabled(containerName);
531 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
534 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
537 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
539 FlowConfig staticFlow = frm.getStaticFlow(name, node);
540 if (staticFlow == null) {
541 throw new ResourceNotFoundException(name + " : " + RestMessages.NOFLOW.toString());
544 Status status = frm.removeStaticFlow(name, node);
545 if (status.isSuccess()) {
546 NorthboundUtils.auditlog("Flow Entry", username, "removed",
547 name + " from Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
548 return Response.noContent().build();
550 return NorthboundUtils.getResponse(status);
554 * Toggle a Flow configuration
556 * @param containerName
557 * Name of the Container (Eg. 'default')
559 * Type of the node being programmed (Eg. 'OF')
561 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
563 * Name of the Static Flow configuration (Eg. 'Flow1')
564 * @return Response as dictated by the HTTP Response code
571 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
575 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
577 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
579 @ResponseCode(code = 200, condition = "Flow Config processed successfully"),
580 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
581 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
582 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
583 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
584 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
585 public Response toggleFlow(@PathParam(value = "containerName") String containerName,
586 @PathParam("nodeType") String nodeType, @PathParam(value = "nodeId") String nodeId,
587 @PathParam(value = "name") String name) {
589 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
590 throw new UnauthorizedException("User is not authorized to perform this operation on container "
594 handleDefaultDisabled(containerName);
596 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
599 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
602 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
604 FlowConfig staticFlow = frm.getStaticFlow(name, node);
605 if (staticFlow == null) {
606 throw new ResourceNotFoundException(name + " : " + RestMessages.NOFLOW.toString());
609 Status status = frm.toggleStaticFlowStatus(staticFlow);
610 if (status.isSuccess()) {
611 NorthboundUtils.auditlog("Flow Entry", username, "toggled",
612 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
614 return NorthboundUtils.getResponse(status);
617 private Node handleNodeAvailability(String containerName, String nodeType, String nodeId) {
619 Node node = Node.fromString(nodeType, nodeId);
621 throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
624 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
627 throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString());
630 if (!sm.getNodes().contains(node)) {
631 throw new ResourceNotFoundException(node.toString() + " : " + RestMessages.NONODE.toString());
636 private void handleDefaultDisabled(String containerName) {
637 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
638 IContainerManager.class, this);
639 if (containerManager == null) {
640 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
642 if (containerName.equals(GlobalConstants.DEFAULT.toString()) && containerManager.hasNonDefaultContainer()) {
643 throw new NotAcceptableException(RestMessages.DEFAULTDISABLED.toString());
647 private void handleResourceCongruence(String resource, String configured) {
648 if (!resource.equals(configured)) {
649 throw new MethodNotAllowedException("Path's resource name conflicts with payload's resource name");