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.MethodNotAllowedException;
37 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
38 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
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.utils.NorthboundUtils;
43 import org.opendaylight.controller.sal.authorization.Privilege;
44 import org.opendaylight.controller.sal.core.Node;
45 import org.opendaylight.controller.sal.utils.GlobalConstants;
46 import org.opendaylight.controller.sal.utils.ServiceHelper;
47 import org.opendaylight.controller.sal.utils.Status;
48 import org.opendaylight.controller.switchmanager.ISwitchManager;
51 * Flow Configuration Northbound API provides capabilities to program flows.
55 * Authentication scheme : <b>HTTP Basic</b><br>
56 * Authentication realm : <b>opendaylight</b><br>
57 * Transport : <b>HTTP and HTTPS</b><br>
59 * HTTPS Authentication is disabled by default.
63 public class FlowProgrammerNorthbound {
65 private String username;
68 public void setSecurityContext(SecurityContext context) {
69 if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
72 protected String getUserName() {
76 private IForwardingRulesManager getForwardingRulesManagerService(
77 String containerName) {
78 IContainerManager containerManager = (IContainerManager) ServiceHelper
79 .getGlobalInstance(IContainerManager.class, this);
80 if (containerManager == null) {
81 throw new ServiceUnavailableException("Container "
82 + RestMessages.SERVICEUNAVAILABLE.toString());
85 boolean found = false;
86 List<String> containerNames = containerManager.getContainerNames();
87 for (String cName : containerNames) {
88 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
94 throw new ResourceNotFoundException(containerName + " "
95 + RestMessages.NOCONTAINER.toString());
98 IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
99 .getInstance(IForwardingRulesManager.class, containerName, this);
102 throw new ServiceUnavailableException("Flow Programmer "
103 + RestMessages.SERVICEUNAVAILABLE.toString());
109 private List<FlowConfig> getStaticFlowsInternal(String containerName,
111 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
114 throw new ServiceUnavailableException("Flow Programmer "
115 + RestMessages.SERVICEUNAVAILABLE.toString());
118 List<FlowConfig> flows = new ArrayList<FlowConfig>();
121 for (FlowConfig flow : frm.getStaticFlows()) {
125 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
126 ISwitchManager.class, containerName, this);
129 throw new ServiceUnavailableException("Switch Manager "
130 + RestMessages.SERVICEUNAVAILABLE.toString());
133 if (!sm.getNodes().contains(node)) {
134 throw new ResourceNotFoundException(node.toString() + " : "
135 + RestMessages.NONODE.toString());
138 for (FlowConfig flow : frm.getStaticFlows(node)) {
146 * Returns a list of Flows configured on the given container
148 * @param containerName
149 * Name of the Container (Eg. 'default')
150 * @return List of flows configured on a given container
157 * http://localhost:8080/controller/nb/v2/flowprogrammer/default
160 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
162 *    <flowConfig>
163 *       <installInHw>true</installInHw>
164 *       <name>flow1</name>
165 *       <node>
166 *          <id>00:00:00:00:00:00:00:01</id>
167 *          <type>OF</type>
168 *       </node>
169 *       <ingressPort>1</ingressPort>
170 *       <priority>500</priority>
171 *       <etherType>0x800</etherType>
172 *       <nwSrc>9.9.1.1</nwSrc>
173 *       <actions>OUTPUT=2</actions>
174 *    </flowConfig>
178 * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
179 * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
183 @Path("/{containerName}")
185 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
186 @TypeHint(FlowConfigs.class)
188 @ResponseCode(code = 200, condition = "Operation successful"),
189 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
190 @ResponseCode(code = 404, condition = "The containerName is not found"),
191 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
192 public FlowConfigs getStaticFlows(
193 @PathParam("containerName") String containerName) {
194 if (!NorthboundUtils.isAuthorized(
195 getUserName(), containerName, Privilege.READ, this)) {
196 throw new UnauthorizedException(
197 "User is not authorized to perform this operation on container "
201 List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName,
203 return new FlowConfigs(flowConfigs);
207 * Returns a list of Flows configured on a Node in a given container
209 * @param containerName
210 * Name of the Container (Eg. 'default')
212 * Type of the node being programmed (Eg. 'OF')
214 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
215 * @return List of flows configured on a Node in a container
222 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
225 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
227 *    <flowConfig>
228 *       <installInHw>true</installInHw>
229 *       <name>flow1</name>
230 *       <node>
231 *          <id>00:00:00:00:00:00:00:01</id>
232 *          <type>OF</type>
233 *       </node>
234 *       <ingressPort>1</ingressPort>
235 *       <priority>500</priority>
236 *       <etherType>0x800</etherType>
237 *       <nwSrc>9.9.1.1</nwSrc>
238 *       <actions>OUTPUT=2</actions>
239 *    </flowConfig>
243 * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
244 * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
248 @Path("/{containerName}/node/{nodeType}/{nodeId}")
250 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
251 @TypeHint(FlowConfigs.class)
253 @ResponseCode(code = 200, condition = "Operation successful"),
254 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
255 @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
256 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
257 public FlowConfigs getStaticFlows(
258 @PathParam("containerName") String containerName,
259 @PathParam("nodeType") String nodeType,
260 @PathParam("nodeId") String nodeId) {
261 if (!NorthboundUtils.isAuthorized(
262 getUserName(), containerName, Privilege.READ, this)) {
263 throw new UnauthorizedException(
264 "User is not authorized to perform this operation on container "
267 Node node = Node.fromString(nodeType, nodeId);
269 throw new ResourceNotFoundException(nodeId + " : "
270 + RestMessages.NONODE.toString());
272 List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
273 return new FlowConfigs(flows);
277 * Returns the flow configuration matching a human-readable name and nodeId
278 * on a given Container.
280 * @param containerName
281 * Name of the Container (Eg. 'default')
283 * Type of the node being programmed (Eg. 'OF')
285 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
287 * Human-readable name for the configured flow (Eg. 'Flow1')
288 * @return Flow configuration matching the name and nodeId on a Container
295 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
298 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
300 *    <installInHw>true</installInHw>
301 *    <name>flow1</name>
302 *    <node>
303 *       <id>00:00:00:00:00:00:00:01</id>
304 *       <type>OF</type>
305 *    </node>
306 *    <ingressPort>1</ingressPort>
307 *    <priority>500</priority>
308 *    <etherType>0x800</etherType>
309 *    <nwSrc>9.9.1.1</nwSrc>
310 *    <actions>OUTPUT=2</actions>
311 * </flowConfig>
314 * {"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
315 * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
319 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
321 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
322 @TypeHint(FlowConfig.class)
324 @ResponseCode(code = 200, condition = "Operation successful"),
325 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
326 @ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
327 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
328 public FlowConfig getStaticFlow(
329 @PathParam("containerName") String containerName,
330 @PathParam("nodeType") String nodeType,
331 @PathParam("nodeId") String nodeId, @PathParam("name") String name) {
332 if (!NorthboundUtils.isAuthorized(
333 getUserName(), containerName, Privilege.READ, this)) {
334 throw new UnauthorizedException(
335 "User is not authorized to perform this operation on container "
338 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
341 throw new ServiceUnavailableException("Flow Programmer "
342 + RestMessages.SERVICEUNAVAILABLE.toString());
345 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
347 FlowConfig staticFlow = frm.getStaticFlow(name, node);
348 if (staticFlow == null) {
349 throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
352 return new FlowConfig(staticFlow);
356 * Add a flow configuration. If a flow by the given name already
357 * exists, this method will respond with a non-successful status response.
359 * @param containerName
360 * Name of the Container (Eg. 'default')
362 * Type of the node being programmed (Eg. 'OF')
364 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
366 * Name of the Static Flow configuration (Eg. 'Flow2')
368 * Flow Configuration in JSON or XML format
369 * @return Response as dictated by the HTTP Response Status code
376 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
380 *    <installInHw>true</installInHw>
381 *    <name>flow1</name>
382 *    <node>
383 *       <id>00:00:00:00:00:00:00:01</id>
384 *       <type>OF</type>
385 *    </node>
386 *    <ingressPort>1</ingressPort>
387 *    <priority>500</priority>
388 *    <etherType>0x800</etherType>
389 *    <nwSrc>9.9.1.1</nwSrc>
390 *    <actions>OUTPUT=2</actions>
391 * </flowConfig>
394 * {"installInHw":"true","name":"flow1","node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},
395 * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
400 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
402 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
404 @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
405 @ResponseCode(code = 400, condition = "Failed to create Static Flow entry due to invalid flow configuration"),
406 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
407 @ResponseCode(code = 404, condition = "The Container Name or nodeId is not found"),
408 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
409 @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name or configuration"),
410 @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
411 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
412 public Response addFlow(
413 @PathParam(value = "containerName") String containerName,
414 @PathParam(value = "name") String name,
415 @PathParam("nodeType") String nodeType,
416 @PathParam(value = "nodeId") String nodeId,
417 @TypeHint(FlowConfig.class) FlowConfig flowConfig) {
419 if (!NorthboundUtils.isAuthorized(
420 getUserName(), containerName, Privilege.WRITE, this)) {
421 throw new UnauthorizedException(
422 "User is not authorized to perform this operation on container "
426 if (flowConfig.getNode() == null) {
427 return Response.status(Response.Status.BAD_REQUEST).entity("Invalid Configuration. Node is null or empty")
430 handleResourceCongruence(name, flowConfig.getName());
431 handleResourceCongruence(nodeId, flowConfig.getNode().getNodeIDString());
432 handleDefaultDisabled(containerName);
434 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
437 throw new ServiceUnavailableException("Flow Programmer "
438 + RestMessages.SERVICEUNAVAILABLE.toString());
441 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
443 FlowConfig staticFlow = frm.getStaticFlow(name, node);
444 if (staticFlow != null) {
445 throw new ResourceConflictException(name + " already exists."
446 + RestMessages.RESOURCECONFLICT.toString());
449 Status status = frm.addStaticFlow(flowConfig);
451 if (status.isSuccess()) {
452 NorthboundUtils.auditlog("Flow", username, "added", name, containerName);
453 return Response.status(Response.Status.CREATED).entity("Success").build();
455 return NorthboundUtils.getResponse(status);
459 * Delete a Flow configuration
461 * @param containerName
462 * Name of the Container (Eg. 'default')
464 * Type of the node being programmed (Eg. 'OF')
466 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
468 * Name of the Static Flow configuration (Eg. 'Flow1')
469 * @return Response as dictated by the HTTP Response code
476 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
481 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
483 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
485 @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
486 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
487 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
488 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
489 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
490 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
491 public Response deleteFlow(
492 @PathParam(value = "containerName") String containerName,
493 @PathParam(value = "name") String name,
494 @PathParam("nodeType") String nodeType,
495 @PathParam(value = "nodeId") String nodeId) {
497 if (!NorthboundUtils.isAuthorized(
498 getUserName(), containerName, Privilege.WRITE, this)) {
499 throw new UnauthorizedException(
500 "User is not authorized to perform this operation on container "
503 handleDefaultDisabled(containerName);
505 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
508 throw new ServiceUnavailableException("Flow Programmer "
509 + RestMessages.SERVICEUNAVAILABLE.toString());
512 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
514 FlowConfig staticFlow = frm.getStaticFlow(name, node);
515 if (staticFlow == null) {
516 throw new ResourceNotFoundException(name + " : "
517 + RestMessages.NOFLOW.toString());
520 Status status = frm.removeStaticFlow(name, node);
521 if (status.isSuccess()) {
522 NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
523 return Response.noContent().build();
525 return NorthboundUtils.getResponse(status);
529 * Toggle a Flow configuration
531 * @param containerName
532 * Name of the Container (Eg. 'default')
534 * Type of the node being programmed (Eg. 'OF')
536 * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
538 * Name of the Static Flow configuration (Eg. 'Flow1')
539 * @return Response as dictated by the HTTP Response code
546 * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
550 @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
552 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
554 @ResponseCode(code = 200, condition = "Flow Config processed successfully"),
555 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
556 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
557 @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
558 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
559 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
560 public Response toggleFlow(
561 @PathParam(value = "containerName") String containerName,
562 @PathParam("nodeType") String nodeType,
563 @PathParam(value = "nodeId") String nodeId,
564 @PathParam(value = "name") String name) {
566 if (!NorthboundUtils.isAuthorized(
567 getUserName(), containerName, Privilege.WRITE, this)) {
568 throw new UnauthorizedException(
569 "User is not authorized to perform this operation on container "
573 handleDefaultDisabled(containerName);
575 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
578 throw new ServiceUnavailableException("Flow Programmer "
579 + RestMessages.SERVICEUNAVAILABLE.toString());
582 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
584 FlowConfig staticFlow = frm.getStaticFlow(name, node);
585 if (staticFlow == null) {
586 throw new ResourceNotFoundException(name + " : "
587 + RestMessages.NOFLOW.toString());
590 Status status = frm.toggleStaticFlowStatus(staticFlow);
591 if (status.isSuccess()) {
592 NorthboundUtils.auditlog("Flow", username, "toggled", name, containerName);
594 return NorthboundUtils.getResponse(status);
597 private Node handleNodeAvailability(String containerName, String nodeType,
600 Node node = Node.fromString(nodeType, nodeId);
602 throw new ResourceNotFoundException(nodeId + " : "
603 + RestMessages.NONODE.toString());
606 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
607 ISwitchManager.class, containerName, this);
610 throw new ServiceUnavailableException("Switch Manager "
611 + RestMessages.SERVICEUNAVAILABLE.toString());
614 if (!sm.getNodes().contains(node)) {
615 throw new ResourceNotFoundException(node.toString() + " : "
616 + RestMessages.NONODE.toString());
621 private void handleDefaultDisabled(String containerName) {
622 IContainerManager containerManager = (IContainerManager) ServiceHelper
623 .getGlobalInstance(IContainerManager.class, this);
624 if (containerManager == null) {
625 throw new InternalServerErrorException(
626 RestMessages.INTERNALERROR.toString());
628 if (containerName.equals(GlobalConstants.DEFAULT.toString())
629 && containerManager.hasNonDefaultContainer()) {
630 throw new NotAcceptableException(
631 RestMessages.DEFAULTDISABLED.toString());
635 private void handleResourceCongruence(String resource, String configured) {
636 if (!resource.equals(configured)) {
637 throw new MethodNotAllowedException("Path's resource name conflicts with payload's resource name");