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.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.ext.ContextResolver;
29 import org.codehaus.enunciate.jaxrs.ResponseCode;
30 import org.codehaus.enunciate.jaxrs.StatusCodes;
31 import org.codehaus.enunciate.jaxrs.TypeHint;
32 import org.opendaylight.controller.containermanager.IContainerManager;
33 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
34 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
35 import org.opendaylight.controller.northbound.commons.RestMessages;
36 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
37 import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException;
38 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
39 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
40 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
41 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
42 import org.opendaylight.controller.northbound.commons.query.QueryContext;
43 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
44 import org.opendaylight.controller.sal.authorization.Privilege;
45 import org.opendaylight.controller.sal.core.Node;
46 import org.opendaylight.controller.sal.utils.GlobalConstants;
47 import org.opendaylight.controller.sal.utils.ServiceHelper;
48 import org.opendaylight.controller.sal.utils.Status;
49 import org.opendaylight.controller.switchmanager.ISwitchManager;
52 * Flow Configuration Northbound API provides capabilities to program flows.
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.
64 public class FlowProgrammerNorthbound {
66 private String username;
68 private QueryContext queryContext;
70 public void setQueryContext(ContextResolver<QueryContext> queryCtxResolver) {
71 if (queryCtxResolver != null) {
72 queryContext = queryCtxResolver.getContext(QueryContext.class);
77 public void setSecurityContext(SecurityContext context) {
78 if (context != null && context.getUserPrincipal() != null) {
79 username = context.getUserPrincipal().getName();
83 protected String getUserName() {
87 private IForwardingRulesManager getForwardingRulesManagerService(String containerName) {
88 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
89 IContainerManager.class, this);
90 if (containerManager == null) {
91 throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString());
94 boolean found = false;
95 List<String> containerNames = containerManager.getContainerNames();
96 for (String cName : containerNames) {
97 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
102 if (found == false) {
103 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
106 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper.getInstance(
107 IForwardingRulesManager.class, containerName, this);
110 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
116 private List<FlowConfig> getStaticFlowsInternal(String containerName, Node node) {
117 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
120 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
123 List<FlowConfig> flows = new ArrayList<FlowConfig>();
126 for (FlowConfig flow : frm.getStaticFlows()) {
130 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
133 throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString());
136 if (!sm.getNodes().contains(node)) {
137 throw new ResourceNotFoundException(node.toString() + " : " + RestMessages.NONODE.toString());
140 for (FlowConfig flow : frm.getStaticFlows(node)) {
148 * Returns a list of Flows configured on the given container
150 * @param containerName
151 * Name of the Container (Eg. 'default')
152 * @return List of flows configured on a given container
159 * http://localhost:8080/controller/nb/v2/flowprogrammer/default
161 * Response body in XML:
162 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
164 *    <flowConfig>
165 *       <installInHw>true</installInHw>
166 *       <name>flow1</name>
167 *       <node>
168 *          <id>00:00:00:00:00:00:00:01</id>
169 *          <type>OF</type>
170 *       </node>
171 *       <ingressPort>1</ingressPort>
172 *       <priority>500</priority>
173 *       <etherType>0x800</etherType>
174 *       <nwSrc>9.9.1.1</nwSrc>
175 *       <actions>OUTPUT=2</actions>
176 *    </flowConfig>
179 * Response body in JSON:
183 * "installInHw": "true",
187 * "id": "00:00:00:00:00:00:00:01"
189 * "ingressPort": "1",
191 * "etherType": "0x800",
201 @Path("/{containerName}")
203 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
204 @TypeHint(FlowConfigs.class)
205 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
206 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
207 @ResponseCode(code = 404, condition = "The containerName is not found"),
208 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable"),
209 @ResponseCode(code = 400, condition = "Incorrect query syntex")})
210 public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName,
211 @QueryParam("_q") String queryString) {
212 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
213 throw new UnauthorizedException("User is not authorized to perform this operation on container "
217 FlowConfigs result = new FlowConfigs(getStaticFlowsInternal(containerName, null));
218 if (queryString != null) {
219 queryContext.createQuery(queryString, FlowConfigs.class)
220 .filter(result, FlowConfig.class);
226 * Returns a list of Flows configured on a Node in a given container
228 * @param containerName
229 * Name of the Container (Eg. 'default')
231 * Type of the node being programmed (Eg. 'OF')
233 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
234 * @return List of flows configured on a Node in a container
241 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
243 * Response body in XML:
244 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
246 *    <flowConfig>
247 *       <installInHw>true</installInHw>
248 *       <name>flow1</name>
249 *       <node>
250 *          <id>00:00:00:00:00:00:00:01</id>
251 *          <type>OF</type>
252 *       </node>
253 *       <ingressPort>1</ingressPort>
254 *       <priority>500</priority>
255 *       <etherType>0x800</etherType>
256 *       <nwSrc>9.9.1.1</nwSrc>
257 *       <actions>OUTPUT=2</actions>
258 *    </flowConfig>
261 * Response body in JSON:
265 * "installInHw": "true",
269 * "id": "00:00:00:00:00:00:00:01"
271 * "ingressPort": "1",
273 * "etherType": "0x800",
283 @Path("/{containerName}/node/{nodeType}/{nodeId}")
285 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
286 @TypeHint(FlowConfigs.class)
287 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
288 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
289 @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
290 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
291 public FlowConfigs getStaticFlows(@PathParam("containerName") String containerName,
292 @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
293 @QueryParam("_q") String queryString) {
294 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
295 throw new UnauthorizedException("User is not authorized to perform this operation on container "
298 Node node = Node.fromString(nodeType, nodeId);
300 throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
302 FlowConfigs flows = new FlowConfigs(getStaticFlowsInternal(containerName, node));
303 if (queryString != null) {
304 queryContext.createQuery(queryString, FlowConfigs.class)
305 .filter(flows, FlowConfig.class);
311 * Returns the flow configuration matching a human-readable name and nodeId
312 * on a given Container.
314 * @param containerName
315 * Name of the Container (Eg. 'default')
317 * Type of the node being programmed (Eg. 'OF')
319 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
321 * Human-readable name for the configured flow (Eg. 'Flow1')
322 * @return Flow configuration matching the name and nodeId on a Container
329 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
331 * Response body in XML:
332 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
334 *    <installInHw>true</installInHw>
335 *    <name>flow1</name>
336 *    <node>
337 *       <id>00:00:00:00:00:00:00:01</id>
338 *       <type>OF</type>
339 *    </node>
340 *    <ingressPort>1</ingressPort>
341 *    <priority>500</priority>
342 *    <etherType>0x800</etherType>
343 *    <nwSrc>9.9.1.1</nwSrc>
344 *    <actions>OUTPUT=2</actions>
345 * </flowConfig>
347 * Response body in JSON:
349 * "installInHw":"true",
352 * "id":"00:00:00:00:00:00:00:01",
357 * "etherType":"0x800",
366 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
368 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
369 @TypeHint(FlowConfig.class)
370 @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
371 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
372 @ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
373 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
374 public FlowConfig getStaticFlow(@PathParam("containerName") String containerName,
375 @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId, @PathParam("name") String name) {
376 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
377 throw new UnauthorizedException("User is not authorized to perform this operation on container "
380 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
383 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
386 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
388 FlowConfig staticFlow = frm.getStaticFlow(name, node);
389 if (staticFlow == null) {
390 throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
393 return new FlowConfig(staticFlow);
397 * Add or Modify a flow configuration. If the flow exists already, it will replace the current flow.
399 * @param containerName
400 * Name of the Container (Eg. 'default')
402 * Type of the node being programmed (Eg. 'OF')
404 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
406 * Name of the Static Flow configuration (Eg. 'Flow2')
408 * Flow Configuration in JSON or XML format
409 * @return Response as dictated by the HTTP Response Status code
416 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
418 * Request body in XML:
420 *    <installInHw>true</installInHw>
421 *    <name>flow1</name>
422 *    <node>
423 *       <id>00:00:00:00:00:00:00:01</id>
424 *       <type>OF</type>
425 *    </node>
426 *    <ingressPort>1</ingressPort>
427 *    <priority>500</priority>
428 *    <etherType>0x800</etherType>
429 *    <nwSrc>9.9.1.1</nwSrc>
430 *    <actions>OUTPUT=2</actions>
431 * </flowConfig>
433 * Request body in JSON:
435 * "installInHw":"true",
438 * "id":"00:00:00:00:00:00:00:01",
443 * "etherType":"0x800",
452 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
454 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
456 @ResponseCode(code = 200, condition = "Static Flow modified successfully"),
457 @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
458 @ResponseCode(code = 400, condition = "Failed to create Static Flow entry due to invalid flow configuration"),
459 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
460 @ResponseCode(code = 404, condition = "The Container Name or nodeId is not found"),
461 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
462 @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name or configuration"),
463 @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
464 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
465 public Response addOrModifyFlow(@PathParam(value = "containerName") String containerName,
466 @PathParam(value = "name") String name, @PathParam("nodeType") String nodeType,
467 @PathParam(value = "nodeId") String nodeId, @TypeHint(FlowConfig.class) FlowConfig flowConfig) {
469 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
470 throw new UnauthorizedException("User is not authorized to perform this operation on container "
474 if (flowConfig.getNode() == null) {
475 return Response.status(Response.Status.BAD_REQUEST).entity("Invalid Configuration. Node is null or empty")
478 handleResourceCongruence(name, flowConfig.getName());
479 handleResourceCongruence(nodeType, flowConfig.getNode().getType());
480 handleResourceCongruence(nodeId, flowConfig.getNode().getID() == null ? null : flowConfig.getNode().getNodeIDString());
481 handleDefaultDisabled(containerName);
483 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
486 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
489 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
492 FlowConfig staticFlow = frm.getStaticFlow(name, node);
494 if (staticFlow == null) {
495 status = frm.addStaticFlow(flowConfig);
496 if(status.isSuccess()){
497 NorthboundUtils.auditlog("Flow Entry", username, "added",
498 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
499 return Response.status(Response.Status.CREATED).entity("Success").build();
502 status = frm.modifyStaticFlow(flowConfig);
503 if(status.isSuccess()){
504 NorthboundUtils.auditlog("Flow Entry", username, "updated",
505 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
506 return NorthboundUtils.getResponse(status);
509 return NorthboundUtils.getResponse(status);
513 * Delete a Flow configuration
515 * @param containerName
516 * Name of the Container (Eg. 'default')
518 * Type of the node being programmed (Eg. 'OF')
520 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
522 * Name of the Static Flow configuration (Eg. 'Flow1')
523 * @return Response as dictated by the HTTP Response code
530 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
535 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
538 @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
539 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
540 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
541 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
542 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
543 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
544 public Response deleteFlow(@PathParam(value = "containerName") String containerName,
545 @PathParam(value = "name") String name, @PathParam("nodeType") String nodeType,
546 @PathParam(value = "nodeId") String nodeId) {
548 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
549 throw new UnauthorizedException("User is not authorized to perform this operation on container "
552 handleDefaultDisabled(containerName);
554 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
557 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
560 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
562 FlowConfig staticFlow = frm.getStaticFlow(name, node);
563 if (staticFlow == null) {
564 throw new ResourceNotFoundException(name + " : " + RestMessages.NOFLOW.toString());
567 Status status = frm.removeStaticFlow(name, node);
568 if (status.isSuccess()) {
569 NorthboundUtils.auditlog("Flow Entry", username, "removed",
570 name + " from Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
571 return Response.noContent().build();
573 return NorthboundUtils.getResponse(status);
577 * Toggle a Flow configuration
579 * @param containerName
580 * Name of the Container (Eg. 'default')
582 * Type of the node being programmed (Eg. 'OF')
584 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
586 * Name of the Static Flow configuration (Eg. 'Flow1')
587 * @return Response as dictated by the HTTP Response code
594 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
598 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
600 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
602 @ResponseCode(code = 200, condition = "Flow Config processed successfully"),
603 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
604 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
605 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
606 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
607 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
608 public Response toggleFlow(@PathParam(value = "containerName") String containerName,
609 @PathParam("nodeType") String nodeType, @PathParam(value = "nodeId") String nodeId,
610 @PathParam(value = "name") String name) {
612 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
613 throw new UnauthorizedException("User is not authorized to perform this operation on container "
617 handleDefaultDisabled(containerName);
619 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
622 throw new ServiceUnavailableException("Flow Programmer " + RestMessages.SERVICEUNAVAILABLE.toString());
625 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
627 FlowConfig staticFlow = frm.getStaticFlow(name, node);
628 if (staticFlow == null) {
629 throw new ResourceNotFoundException(name + " : " + RestMessages.NOFLOW.toString());
632 Status status = frm.toggleStaticFlowStatus(staticFlow);
633 if (status.isSuccess()) {
634 NorthboundUtils.auditlog("Flow Entry", username, "toggled",
635 name + " on Node " + NorthboundUtils.getNodeDesc(node, containerName, this), containerName);
637 return NorthboundUtils.getResponse(status);
640 private Node handleNodeAvailability(String containerName, String nodeType, String nodeId) {
642 Node node = Node.fromString(nodeType, nodeId);
644 throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
647 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
650 throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString());
653 if (!sm.getNodes().contains(node)) {
654 throw new ResourceNotFoundException(node.toString() + " : " + RestMessages.NONODE.toString());
659 private void handleDefaultDisabled(String containerName) {
660 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
661 IContainerManager.class, this);
662 if (containerManager == null) {
663 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
665 if (containerName.equals(GlobalConstants.DEFAULT.toString()) && containerManager.hasNonDefaultContainer()) {
666 throw new NotAcceptableException(RestMessages.DEFAULTDISABLED.toString());
670 private void handleResourceCongruence(String resource, String configured) {
671 if (!resource.equals(configured)) {
672 throw new MethodNotAllowedException("Path's resource name conflicts with payload's resource name");