Merge "Issue in flow programming: Add a flow, uninstall it, try to remove it. Remove...
[controller.git] / opendaylight / northbound / flowprogrammer / src / main / java / org / opendaylight / controller / flowprogrammer / northbound / FlowProgrammerNorthbound.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.flowprogrammer.northbound;
11
12 import java.util.ArrayList;
13 import java.util.List;
14
15 import javax.ws.rs.Consumes;
16 import javax.ws.rs.DELETE;
17 import javax.ws.rs.GET;
18 import javax.ws.rs.POST;
19 import javax.ws.rs.PUT;
20 import javax.ws.rs.Path;
21 import javax.ws.rs.PathParam;
22 import javax.ws.rs.Produces;
23 import javax.ws.rs.core.MediaType;
24 import javax.ws.rs.core.Response;
25 import javax.xml.bind.JAXBElement;
26
27 import org.codehaus.enunciate.jaxrs.ResponseCode;
28 import org.codehaus.enunciate.jaxrs.StatusCodes;
29 import org.codehaus.enunciate.jaxrs.TypeHint;
30 import org.opendaylight.controller.containermanager.IContainerManager;
31 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
32 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
33 import org.opendaylight.controller.northbound.commons.RestMessages;
34 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
35 import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
36 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
37 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
38 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
39 import org.opendaylight.controller.sal.core.Node;
40 import org.opendaylight.controller.sal.utils.GlobalConstants;
41 import org.opendaylight.controller.sal.utils.ServiceHelper;
42 import org.opendaylight.controller.sal.utils.Status;
43 import org.opendaylight.controller.switchmanager.ISwitchManager;
44
45 /**
46  * Flow Configuration Northbound API
47  *
48  * <br><br>
49  * Authentication scheme : <b>HTTP Basic</b><br>
50  * Authentication realm : <b>opendaylight</b><br>
51  * Transport : <b>HTTP and HTTPS</b><br>
52  * <br>
53  * HTTPS Authentication is disabled by default. Administrator can enable it in tomcat-server.xml after adding 
54  * a proper keystore / SSL certificate from a trusted authority.<br>
55  * More info : http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
56  *
57  */
58 @Path("/")
59 public class FlowProgrammerNorthbound {
60
61     private IForwardingRulesManager getForwardingRulesManagerService(
62             String containerName) {
63         IContainerManager containerManager = (IContainerManager) ServiceHelper
64                 .getGlobalInstance(IContainerManager.class, this);
65         if (containerManager == null) {
66             throw new ServiceUnavailableException("Container "
67                     + RestMessages.SERVICEUNAVAILABLE.toString());
68         }
69
70         boolean found = false;
71         List<String> containerNames = containerManager.getContainerNames();
72         for (String cName : containerNames) {
73             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
74                 found = true;
75             }
76         }
77
78         if (found == false) {
79             throw new ResourceNotFoundException(containerName + " "
80                     + RestMessages.NOCONTAINER.toString());
81         }
82
83         IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
84                 .getInstance(IForwardingRulesManager.class, containerName, this);
85
86         if (frm == null) {
87             throw new ServiceUnavailableException("Flow Programmer "
88                     + RestMessages.SERVICEUNAVAILABLE.toString());
89         }
90
91         return frm;
92     }
93
94     private List<FlowConfig> getStaticFlowsInternal(String containerName,
95             Node node) {
96         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
97
98         if (frm == null) {
99             throw new ServiceUnavailableException("Flow Programmer "
100                     + RestMessages.SERVICEUNAVAILABLE.toString());
101         }
102
103         List<FlowConfig> flows = new ArrayList<FlowConfig>();
104
105         if (node == null) {
106             for (FlowConfig flow : frm.getStaticFlows()) {
107                 flows.add(flow);
108             }
109         } else {
110             ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
111                     ISwitchManager.class, containerName, this);
112
113             if (sm == null) {
114                 throw new ServiceUnavailableException("Switch Manager "
115                         + RestMessages.SERVICEUNAVAILABLE.toString());
116             }
117
118             if (!sm.getNodes().contains(node)) {
119                 throw new ResourceNotFoundException(node.toString() + " : "
120                         + RestMessages.NONODE.toString());
121             }
122
123             for (FlowConfig flow : frm.getStaticFlows(node)) {
124                 flows.add(flow);
125             }
126         }
127         return flows;
128     }
129
130     /**
131      * Returns a list of Flows configured on the given container
132      *
133      * @param containerName Name of the Container. The Container name for the base controller is "default".
134      * @return List of configured flows configured on a given container
135      */
136     @Path("/{containerName}")
137     @GET
138     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
139     @TypeHint(FlowConfigs.class)
140     @StatusCodes( {
141             @ResponseCode(code = 200, condition = "Operation successful"),
142             @ResponseCode(code = 404, condition = "The containerName is not found"),
143             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
144     public FlowConfigs getStaticFlows(
145             @PathParam("containerName") String containerName) {
146         List<FlowConfig> flowConfigs = getStaticFlowsInternal(containerName, null);
147         return new FlowConfigs(flowConfigs);
148     }
149
150     /**
151      * Returns a list of Flows configured on a Node in a given container
152      *
153      * @param containerName Name of the Container. The Container name
154      * for the base controller is "default".
155      * @param nodeType Type of the node being programmed
156      * @param nodeId Node Identifier
157      * @return List of configured flows configured on a Node in a container
158      */
159     @Path("/{containerName}/{nodeType}/{nodeId}")
160     @GET
161     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
162     @TypeHint(FlowConfigs.class)
163     @StatusCodes( {
164             @ResponseCode(code = 200, condition = "Operation successful"),
165             @ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
166             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
167     public FlowConfigs getStaticFlows(
168             @PathParam("containerName") String containerName,
169             @PathParam("nodeType") String nodeType,
170             @PathParam("nodeId") String nodeId) {
171         Node node = Node.fromString(nodeType, nodeId);
172         if (node == null) {
173             throw new ResourceNotFoundException(nodeId + " : "
174                     + RestMessages.NONODE.toString());
175         }
176         List<FlowConfig> flows = getStaticFlowsInternal(containerName, node);
177         return new FlowConfigs(flows);
178     }
179
180     /**
181      * Returns the flow configuration matching a human-readable name and nodeId on a
182      * given Container.
183      *
184      * @param containerName Name of the Container. The Container name
185      * for the base controller is "default".
186      * @param nodeType Type of the node being programmed
187      * @param nodeId Node Identifier
188      * @param name Human-readable name for the configured flow.
189      * @return Flow configuration matching the name and nodeId on a Container
190      */
191     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
192     @GET
193     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
194     @TypeHint(FlowConfig.class)
195     @StatusCodes( {
196             @ResponseCode(code = 200, condition = "Operation successful"),
197             @ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
198             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
199     public FlowConfig getStaticFlow(
200             @PathParam("containerName") String containerName,
201             @PathParam("nodeType") String nodeType,
202             @PathParam("nodeId") String nodeId,
203             @PathParam("name") String name) {
204         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
205
206         if (frm == null) {
207             throw new ServiceUnavailableException("Flow Programmer "
208                     + RestMessages.SERVICEUNAVAILABLE.toString());
209         }
210
211         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
212
213         FlowConfig staticFlow = frm.getStaticFlow(name, node);
214         if (staticFlow == null) {
215             throw new ResourceNotFoundException(RestMessages.NOFLOW.toString());
216         }
217
218         return new FlowConfig(staticFlow);
219     }
220
221     /**
222      * Add a flow configuration
223      *
224      * @param containerName Name of the Container. The Container name
225      * for the base controller is "default".
226      * @param nodeType Type of the node being programmed
227      * @param nodeId Node Identifier
228      * @param name Name of the Static Flow configuration
229      * @param FlowConfig Flow Configuration in JSON or XML format
230      * @return Response as dictated by the HTTP Response Status code
231      */
232
233     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
234     @POST
235     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
236     @StatusCodes( {
237             @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
238             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
239             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
240             @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name"),
241             @ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
242             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
243     public Response addFlow(
244             @PathParam(value = "containerName") String containerName,
245             @PathParam(value = "name") String name,
246             @PathParam("nodeType") String nodeType,
247             @PathParam(value = "nodeId") String nodeId,
248             @TypeHint(FlowConfig.class) JAXBElement<FlowConfig> flowConfig) {
249
250         handleDefaultDisabled(containerName);
251
252         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
253
254         if (frm == null) {
255             throw new ServiceUnavailableException("Flow Programmer "
256                     + RestMessages.SERVICEUNAVAILABLE.toString());
257         }
258
259         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
260
261         FlowConfig staticFlow = frm.getStaticFlow(name, node);
262         if (staticFlow != null) {
263             throw new ResourceConflictException(name + " already exists."
264                     + RestMessages.RESOURCECONFLICT.toString());
265         }
266
267         Status status = frm.addStaticFlow(flowConfig.getValue(), false);
268         if (status.isSuccess()) {
269             return Response.status(Response.Status.CREATED).build();
270         }
271         throw new InternalServerErrorException(status.getDescription());
272     }
273
274     /**
275      * Delete a Flow configuration
276      *
277      * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
278      *
279      * @param containerName Name of the Container. The Container name
280      * for the base controller is "default".
281      * @param nodeType Type of the node being programmed
282      * @param nodeId Node Identifier
283      * @param name Name of the Static Flow configuration
284      * @return Response as dictated by the HTTP Response code
285      */
286
287     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
288     @DELETE
289     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
290     @StatusCodes( {
291             @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
292             @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
293             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
294             @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
295             @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
296     public Response deleteFlow(
297             @PathParam(value = "containerName") String containerName,
298             @PathParam(value = "name") String name,
299             @PathParam("nodeType") String nodeType,
300             @PathParam(value = "nodeId") String nodeId) {
301
302         handleDefaultDisabled(containerName);
303
304         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
305
306         if (frm == null) {
307             throw new ServiceUnavailableException("Flow Programmer "
308                     + RestMessages.SERVICEUNAVAILABLE.toString());
309         }
310
311         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
312
313         FlowConfig staticFlow = frm.getStaticFlow(name, node);
314         if (staticFlow == null) {
315             throw new ResourceNotFoundException(name + " : "
316                     + RestMessages.NOFLOW.toString());
317         }
318
319         Status status = frm.removeStaticFlow(name, node);
320         if (status.isSuccess()) {
321             return Response.ok().build();
322         }
323         throw new InternalServerErrorException(status.getDescription());
324     }
325
326     /**
327      * Toggle a Flow configuration
328      *
329      * @param containerName Name of the Container. The Container name
330      * for the base controller is "default".
331      * @param nodeType Type of the node being programmed
332      * @param nodeId Node Identifier
333      * @param name Name of the Static Flow configuration
334      * @return Response as dictated by the HTTP Response code
335      */
336
337     @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
338     @PUT
339     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
340     @StatusCodes( {
341             @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
342             @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
343             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
344             @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
345             @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
346     public Response toggleFlow(
347             @PathParam(value = "containerName") String containerName,
348             @PathParam("nodeType") String nodeType,
349             @PathParam(value = "nodeId") String nodeId,
350             @PathParam(value = "name") String name) {
351
352         handleDefaultDisabled(containerName);
353
354         IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
355
356         if (frm == null) {
357             throw new ServiceUnavailableException("Flow Programmer "
358                     + RestMessages.SERVICEUNAVAILABLE.toString());
359         }
360
361         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
362
363         FlowConfig staticFlow = frm.getStaticFlow(name, node);
364         if (staticFlow == null) {
365             throw new ResourceNotFoundException(name + " : "
366                     + RestMessages.NOFLOW.toString());
367         }
368
369         Status status = frm.toggleStaticFlowStatus(staticFlow);
370         if (status.isSuccess()) {
371             return Response.ok().build();
372         }
373         throw new InternalServerErrorException(status.getDescription());
374     }
375
376     private Node handleNodeAvailability(String containerName, String nodeType,
377                                         String nodeId) {
378
379         Node node = Node.fromString(nodeType, nodeId);
380         if (node == null) {
381             throw new ResourceNotFoundException(nodeId + " : "
382                     + RestMessages.NONODE.toString());
383         }
384
385         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
386                 ISwitchManager.class, containerName, this);
387
388         if (sm == null) {
389             throw new ServiceUnavailableException("Switch Manager "
390                     + RestMessages.SERVICEUNAVAILABLE.toString());
391         }
392
393         if (!sm.getNodes().contains(node)) {
394             throw new ResourceNotFoundException(node.toString() + " : "
395                     + RestMessages.NONODE.toString());
396         }
397         return node;
398     }
399
400     private void handleDefaultDisabled(String containerName) {
401         IContainerManager containerManager = (IContainerManager) ServiceHelper
402                 .getGlobalInstance(IContainerManager.class, this);
403         if (containerManager == null) {
404             throw new InternalServerErrorException(RestMessages.INTERNALERROR
405                     .toString());
406         }
407         if (containerName.equals(GlobalConstants.DEFAULT.toString())
408                 && containerManager.hasNonDefaultContainer()) {
409             throw new NotAcceptableException(RestMessages.DEFAULTDISABLED
410                     .toString());
411         }
412     }
413
414 }