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());
326 if (status.isSuccess()) {
327 NorthboundUtils.auditlog("Flow", username, "added", name, containerName);
328 return Response.status(Response.Status.CREATED).build();
330 throw new InternalServerErrorException(status.getDescription());
334 * Delete a Flow configuration
336 * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
338 * @param containerName
339 * Name of the Container. The Container name for the base
340 * controller is "default".
342 * Type of the node being programmed
346 * Name of the Static Flow configuration
347 * @return Response as dictated by the HTTP Response code
350 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
352 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
354 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
355 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
356 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
357 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
358 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
359 public Response deleteFlow(
360 @PathParam(value = "containerName") String containerName,
361 @PathParam(value = "name") String name,
362 @PathParam("nodeType") String nodeType,
363 @PathParam(value = "nodeId") String nodeId) {
365 if (!NorthboundUtils.isAuthorized(
366 getUserName(), containerName, Privilege.WRITE, this)) {
367 throw new UnauthorizedException(
368 "User is not authorized to perform this operation on container "
371 handleDefaultDisabled(containerName);
373 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
376 throw new ServiceUnavailableException("Flow Programmer "
377 + RestMessages.SERVICEUNAVAILABLE.toString());
380 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
382 FlowConfig staticFlow = frm.getStaticFlow(name, node);
383 if (staticFlow == null) {
384 throw new ResourceNotFoundException(name + " : "
385 + RestMessages.NOFLOW.toString());
388 Status status = frm.removeStaticFlow(name, node);
389 if (status.isSuccess()) {
390 NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
391 return Response.ok().build();
393 throw new InternalServerErrorException(status.getDescription());
397 * Toggle a Flow configuration
399 * @param containerName
400 * Name of the Container. The Container name for the base
401 * controller is "default".
403 * Type of the node being programmed
407 * Name of the Static Flow configuration
408 * @return Response as dictated by the HTTP Response code
411 @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
413 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
415 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
416 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
417 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
418 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
419 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
420 public Response toggleFlow(
421 @PathParam(value = "containerName") String containerName,
422 @PathParam("nodeType") String nodeType,
423 @PathParam(value = "nodeId") String nodeId,
424 @PathParam(value = "name") String name) {
426 if (!NorthboundUtils.isAuthorized(
427 getUserName(), containerName, Privilege.WRITE, this)) {
428 throw new UnauthorizedException(
429 "User is not authorized to perform this operation on container "
433 handleDefaultDisabled(containerName);
435 IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
438 throw new ServiceUnavailableException("Flow Programmer "
439 + RestMessages.SERVICEUNAVAILABLE.toString());
442 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
444 FlowConfig staticFlow = frm.getStaticFlow(name, node);
445 if (staticFlow == null) {
446 throw new ResourceNotFoundException(name + " : "
447 + RestMessages.NOFLOW.toString());
450 Status status = frm.toggleStaticFlowStatus(staticFlow);
451 if (status.isSuccess()) {
452 NorthboundUtils.auditlog("Flow", username, "toggled", name, containerName);
453 return Response.ok().build();
455 throw new InternalServerErrorException(status.getDescription());
458 private Node handleNodeAvailability(String containerName, String nodeType,
461 Node node = Node.fromString(nodeType, nodeId);
463 throw new ResourceNotFoundException(nodeId + " : "
464 + RestMessages.NONODE.toString());
467 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
468 ISwitchManager.class, containerName, this);
471 throw new ServiceUnavailableException("Switch Manager "
472 + RestMessages.SERVICEUNAVAILABLE.toString());
475 if (!sm.getNodes().contains(node)) {
476 throw new ResourceNotFoundException(node.toString() + " : "
477 + RestMessages.NONODE.toString());
482 private void handleDefaultDisabled(String containerName) {
483 IContainerManager containerManager = (IContainerManager) ServiceHelper
484 .getGlobalInstance(IContainerManager.class, this);
485 if (containerManager == null) {
486 throw new InternalServerErrorException(
487 RestMessages.INTERNALERROR.toString());
489 if (containerName.equals(GlobalConstants.DEFAULT.toString())
490 && containerManager.hasNonDefaultContainer()) {
491 throw new NotAcceptableException(
492 RestMessages.DEFAULTDISABLED.toString());