Merge "Cleaup Statistics northbound and add exmaples"
[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
326         if (status.isSuccess()) {
327             NorthboundUtils.auditlog("Flow", username, "added", name, containerName);
328             return Response.status(Response.Status.CREATED).build();
329         }
330         throw new InternalServerErrorException(status.getDescription());
331     }
332
333     /**
334      * Delete a Flow configuration
335      *
336      * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
337      *
338      * @param containerName
339      *            Name of the Container. The Container name for the base
340      *            controller is "default".
341      * @param nodeType
342      *            Type of the node being programmed
343      * @param nodeId
344      *            Node Identifier
345      * @param name
346      *            Name of the Static Flow configuration
347      * @return Response as dictated by the HTTP Response code
348      */
349
350     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
351     @DELETE
352     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
353     @StatusCodes({
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) {
364
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 "
369                             + containerName);
370         }
371         handleDefaultDisabled(containerName);
372
373         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
374
375         if (frm == null) {
376             throw new ServiceUnavailableException("Flow Programmer "
377                     + RestMessages.SERVICEUNAVAILABLE.toString());
378         }
379
380         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
381
382         FlowConfig staticFlow = frm.getStaticFlow(name, node);
383         if (staticFlow == null) {
384             throw new ResourceNotFoundException(name + " : "
385                     + RestMessages.NOFLOW.toString());
386         }
387
388         Status status = frm.removeStaticFlow(name, node);
389         if (status.isSuccess()) {
390             NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
391             return Response.ok().build();
392         }
393         throw new InternalServerErrorException(status.getDescription());
394     }
395
396     /**
397      * Toggle a Flow configuration
398      *
399      * @param containerName
400      *            Name of the Container. The Container name for the base
401      *            controller is "default".
402      * @param nodeType
403      *            Type of the node being programmed
404      * @param nodeId
405      *            Node Identifier
406      * @param name
407      *            Name of the Static Flow configuration
408      * @return Response as dictated by the HTTP Response code
409      */
410
411     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
412     @PUT
413     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
414     @StatusCodes({
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) {
425
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 "
430                             + containerName);
431         }
432
433         handleDefaultDisabled(containerName);
434
435         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
436
437         if (frm == null) {
438             throw new ServiceUnavailableException("Flow Programmer "
439                     + RestMessages.SERVICEUNAVAILABLE.toString());
440         }
441
442         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
443
444         FlowConfig staticFlow = frm.getStaticFlow(name, node);
445         if (staticFlow == null) {
446             throw new ResourceNotFoundException(name + " : "
447                     + RestMessages.NOFLOW.toString());
448         }
449
450         Status status = frm.toggleStaticFlowStatus(staticFlow);
451         if (status.isSuccess()) {
452             NorthboundUtils.auditlog("Flow", username, "toggled", name, containerName);
453             return Response.ok().build();
454         }
455         throw new InternalServerErrorException(status.getDescription());
456     }
457
458     private Node handleNodeAvailability(String containerName, String nodeType,
459             String nodeId) {
460
461         Node node = Node.fromString(nodeType, nodeId);
462         if (node == null) {
463             throw new ResourceNotFoundException(nodeId + " : "
464                     + RestMessages.NONODE.toString());
465         }
466
467         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
468                 ISwitchManager.class, containerName, this);
469
470         if (sm == null) {
471             throw new ServiceUnavailableException("Switch Manager "
472                     + RestMessages.SERVICEUNAVAILABLE.toString());
473         }
474
475         if (!sm.getNodes().contains(node)) {
476             throw new ResourceNotFoundException(node.toString() + " : "
477                     + RestMessages.NONODE.toString());
478         }
479         return node;
480     }
481
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());
488         }
489         if (containerName.equals(GlobalConstants.DEFAULT.toString())
490                 && containerManager.hasNonDefaultContainer()) {
491             throw new NotAcceptableException(
492                     RestMessages.DEFAULTDISABLED.toString());
493         }
494     }
495
496 }