Proactive static flows have to be allowed in container mode
[controller.git] / opendaylight / northbound / flowprogrammer / src / main / java / org / opendaylight / controller / flowprogrammer / northbound / FlowProgrammerNorthbound.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.flowprogrammer.northbound;
10
11 import java.util.ArrayList;
12 import java.util.List;
13
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;
27
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;
48
49 /**
50  * Flow Configuration Northbound API
51  *
52  * <br>
53  * <br>
54  * Authentication scheme : <b>HTTP Basic</b><br>
55  * Authentication realm : <b>opendaylight</b><br>
56  * Transport : <b>HTTP and HTTPS</b><br>
57  * <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>
61  * More info :
62  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
63  *
64  */
65 @Path("/")
66 public class FlowProgrammerNorthbound {
67
68     private String username;
69
70     @Context
71     public void setSecurityContext(SecurityContext context) {
72         username = context.getUserPrincipal().getName();
73     }
74
75     protected String getUserName() {
76         return username;
77     }
78
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());
86         }
87
88         boolean found = false;
89         List<String> containerNames = containerManager.getContainerNames();
90         for (String cName : containerNames) {
91             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
92                 found = true;
93             }
94         }
95
96         if (found == false) {
97             throw new ResourceNotFoundException(containerName + " "
98                     + RestMessages.NOCONTAINER.toString());
99         }
100
101         IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
102                 .getInstance(IForwardingRulesManager.class, containerName, this);
103
104         if (frm == null) {
105             throw new ServiceUnavailableException("Flow Programmer "
106                     + RestMessages.SERVICEUNAVAILABLE.toString());
107         }
108
109         return frm;
110     }
111
112     private List<FlowConfig> getStaticFlowsInternal(String containerName,
113             Node node) {
114         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
115
116         if (frm == null) {
117             throw new ServiceUnavailableException("Flow Programmer "
118                     + RestMessages.SERVICEUNAVAILABLE.toString());
119         }
120
121         List<FlowConfig> flows = new ArrayList<FlowConfig>();
122
123         if (node == null) {
124             for (FlowConfig flow : frm.getStaticFlows()) {
125                 flows.add(flow);
126             }
127         } else {
128             ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
129                     ISwitchManager.class, containerName, this);
130
131             if (sm == null) {
132                 throw new ServiceUnavailableException("Switch Manager "
133                         + RestMessages.SERVICEUNAVAILABLE.toString());
134             }
135
136             if (!sm.getNodes().contains(node)) {
137                 throw new ResourceNotFoundException(node.toString() + " : "
138                         + RestMessages.NONODE.toString());
139             }
140
141             for (FlowConfig flow : frm.getStaticFlows(node)) {
142                 flows.add(flow);
143             }
144         }
145         return flows;
146     }
147
148     /**
149      * Returns a list of Flows configured on the given container
150      *
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
155      */
156     @Path("/{containerName}")
157     @GET
158     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
159     @TypeHint(FlowConfigs.class)
160     @StatusCodes({
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 "
170                             + containerName);
171         }
172
173         List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName,
174                 null);
175         return new FlowConfigs(flowConfigs);
176     }
177
178     /**
179      * Returns a list of Flows configured on a Node in a given container
180      *
181      * @param containerName
182      *            Name of the Container. The Container name for the base
183      *            controller is "default".
184      * @param nodeType
185      *            Type of the node being programmed
186      * @param nodeId
187      *            Node Identifier
188      * @return List of configured flows configured on a Node in a container
189      */
190     @Path("/{containerName}/{nodeType}/{nodeId}")
191     @GET
192     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
193     @TypeHint(FlowConfigs.class)
194     @StatusCodes({
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 "
206                             + containerName);
207         }
208         Node node = Node.fromString(nodeType, nodeId);
209         if (node == null) {
210             throw new ResourceNotFoundException(nodeId + " : "
211                     + RestMessages.NONODE.toString());
212         }
213         List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
214         return new FlowConfigs(flows);
215     }
216
217     /**
218      * Returns the flow configuration matching a human-readable name and nodeId
219      * on a given Container.
220      *
221      * @param containerName
222      *            Name of the Container. The Container name for the base
223      *            controller is "default".
224      * @param nodeType
225      *            Type of the node being programmed
226      * @param nodeId
227      *            Node Identifier
228      * @param name
229      *            Human-readable name for the configured flow.
230      * @return Flow configuration matching the name and nodeId on a Container
231      */
232     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
233     @GET
234     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
235     @TypeHint(FlowConfig.class)
236     @StatusCodes({
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 "
248                             + containerName);
249         }
250         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
251
252         if (frm == null) {
253             throw new ServiceUnavailableException("Flow Programmer "
254                     + RestMessages.SERVICEUNAVAILABLE.toString());
255         }
256
257         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
258
259         FlowConfig staticFlow = frm.getStaticFlow(name, node);
260         if (staticFlow == null) {
261             throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
262         }
263
264         return new FlowConfig(staticFlow);
265     }
266
267     /**
268      * Add a flow configuration
269      *
270      * @param containerName
271      *            Name of the Container. The Container name for the base
272      *            controller is "default".
273      * @param nodeType
274      *            Type of the node being programmed
275      * @param nodeId
276      *            Node Identifier
277      * @param name
278      *            Name of the Static Flow configuration
279      * @param FlowConfig
280      *            Flow Configuration in JSON or XML format
281      * @return Response as dictated by the HTTP Response Status code
282      */
283
284     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
285     @POST
286     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
287     @StatusCodes({
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) {
300
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 "
305                             + containerName);
306         }
307         handleDefaultDisabled(containerName);
308
309         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
310
311         if (frm == null) {
312             throw new ServiceUnavailableException("Flow Programmer "
313                     + RestMessages.SERVICEUNAVAILABLE.toString());
314         }
315
316         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
317
318         FlowConfig staticFlow = frm.getStaticFlow(name, node);
319         if (staticFlow != null) {
320             throw new ResourceConflictException(name + " already exists."
321                     + RestMessages.RESOURCECONFLICT.toString());
322         }
323
324         Status status = frm.addStaticFlow(flowConfig.getValue());
325         if (status.isSuccess()) {
326             return Response.status(Response.Status.CREATED).build();
327         }
328         throw new InternalServerErrorException(status.getDescription());
329     }
330
331     /**
332      * Delete a Flow configuration
333      *
334      * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
335      *
336      * @param containerName
337      *            Name of the Container. The Container name for the base
338      *            controller is "default".
339      * @param nodeType
340      *            Type of the node being programmed
341      * @param nodeId
342      *            Node Identifier
343      * @param name
344      *            Name of the Static Flow configuration
345      * @return Response as dictated by the HTTP Response code
346      */
347
348     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
349     @DELETE
350     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
351     @StatusCodes({
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) {
362
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 "
367                             + containerName);
368         }
369         handleDefaultDisabled(containerName);
370
371         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
372
373         if (frm == null) {
374             throw new ServiceUnavailableException("Flow Programmer "
375                     + RestMessages.SERVICEUNAVAILABLE.toString());
376         }
377
378         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
379
380         FlowConfig staticFlow = frm.getStaticFlow(name, node);
381         if (staticFlow == null) {
382             throw new ResourceNotFoundException(name + " : "
383                     + RestMessages.NOFLOW.toString());
384         }
385
386         Status status = frm.removeStaticFlow(name, node);
387         if (status.isSuccess()) {
388             return Response.ok().build();
389         }
390         throw new InternalServerErrorException(status.getDescription());
391     }
392
393     /**
394      * Toggle a Flow configuration
395      *
396      * @param containerName
397      *            Name of the Container. The Container name for the base
398      *            controller is "default".
399      * @param nodeType
400      *            Type of the node being programmed
401      * @param nodeId
402      *            Node Identifier
403      * @param name
404      *            Name of the Static Flow configuration
405      * @return Response as dictated by the HTTP Response code
406      */
407
408     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
409     @PUT
410     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
411     @StatusCodes({
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) {
422
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 "
427                             + containerName);
428         }
429
430         handleDefaultDisabled(containerName);
431
432         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
433
434         if (frm == null) {
435             throw new ServiceUnavailableException("Flow Programmer "
436                     + RestMessages.SERVICEUNAVAILABLE.toString());
437         }
438
439         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
440
441         FlowConfig staticFlow = frm.getStaticFlow(name, node);
442         if (staticFlow == null) {
443             throw new ResourceNotFoundException(name + " : "
444                     + RestMessages.NOFLOW.toString());
445         }
446
447         Status status = frm.toggleStaticFlowStatus(staticFlow);
448         if (status.isSuccess()) {
449             return Response.ok().build();
450         }
451         throw new InternalServerErrorException(status.getDescription());
452     }
453
454     private Node handleNodeAvailability(String containerName, String nodeType,
455             String nodeId) {
456
457         Node node = Node.fromString(nodeType, nodeId);
458         if (node == null) {
459             throw new ResourceNotFoundException(nodeId + " : "
460                     + RestMessages.NONODE.toString());
461         }
462
463         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
464                 ISwitchManager.class, containerName, this);
465
466         if (sm == null) {
467             throw new ServiceUnavailableException("Switch Manager "
468                     + RestMessages.SERVICEUNAVAILABLE.toString());
469         }
470
471         if (!sm.getNodes().contains(node)) {
472             throw new ResourceNotFoundException(node.toString() + " : "
473                     + RestMessages.NONODE.toString());
474         }
475         return node;
476     }
477
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());
484         }
485         if (containerName.equals(GlobalConstants.DEFAULT.toString())
486                 && containerManager.hasNonDefaultContainer()) {
487             throw new NotAcceptableException(
488                     RestMessages.DEFAULTDISABLED.toString());
489         }
490     }
491
492 }