14eaedd63d2829bd7b29a15a2d26f1f66fbcf00b
[controller.git] / opendaylight / northbound / networkconfiguration / neutron / src / main / java / org / opendaylight / controller / networkconfig / neutron / northbound / NeutronFloatingIPsNorthbound.java
1 /*\r
2  * Copyright IBM Corporation, 2013.  All rights reserved.\r
3  *\r
4  * This program and the accompanying materials are made available under the\r
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
6  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
7  */\r
8 \r
9 package org.opendaylight.controller.networkconfig.neutron.northbound;\r
10 \r
11 import java.util.ArrayList;\r
12 import java.util.Iterator;\r
13 import java.util.List;\r
14 import javax.ws.rs.Consumes;\r
15 import javax.ws.rs.DELETE;\r
16 import javax.ws.rs.GET;\r
17 import javax.ws.rs.POST;\r
18 import javax.ws.rs.PUT;\r
19 import javax.ws.rs.Path;\r
20 import javax.ws.rs.PathParam;\r
21 import javax.ws.rs.Produces;\r
22 import javax.ws.rs.QueryParam;\r
23 import javax.ws.rs.core.MediaType;\r
24 import javax.ws.rs.core.Response;\r
25 \r
26 import org.codehaus.enunciate.jaxrs.ResponseCode;\r
27 import org.codehaus.enunciate.jaxrs.StatusCodes;\r
28 import org.opendaylight.controller.networkconfig.neutron.INeutronFloatingIPAware;\r
29 import org.opendaylight.controller.networkconfig.neutron.INeutronFloatingIPCRUD;\r
30 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;\r
31 import org.opendaylight.controller.networkconfig.neutron.INeutronPortAware;\r
32 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;\r
33 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;\r
34 import org.opendaylight.controller.networkconfig.neutron.NeutronFloatingIP;\r
35 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;\r
36 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;\r
37 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;\r
38 import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;\r
39 import org.opendaylight.controller.northbound.commons.RestMessages;\r
40 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;\r
41 import org.opendaylight.controller.sal.utils.ServiceHelper;\r
42 \r
43 /**\r
44  * Open DOVE Northbound REST APIs.<br>\r
45  * This class provides REST APIs for managing the open DOVE\r
46  *\r
47  * <br>\r
48  * <br>\r
49  * Authentication scheme : <b>HTTP Basic</b><br>\r
50  * Authentication realm : <b>opendaylight</b><br>\r
51  * Transport : <b>HTTP and HTTPS</b><br>\r
52  * <br>\r
53  * HTTPS Authentication is disabled by default. Administrator can enable it in\r
54  * tomcat-server.xml after adding a proper keystore / SSL certificate from a\r
55  * trusted authority.<br>\r
56  * More info :\r
57  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration\r
58  *\r
59  */\r
60 \r
61 @Path("/floatingips")\r
62 public class NeutronFloatingIPsNorthbound {\r
63 \r
64     private NeutronFloatingIP extractFields(NeutronFloatingIP o, List<String> fields) {\r
65         return o.extractFields(fields);\r
66     }\r
67 \r
68     /**\r
69      * Returns a list of all FloatingIPs */\r
70 \r
71     @GET\r
72     @Produces({ MediaType.APPLICATION_JSON })\r
73     @StatusCodes({\r
74             @ResponseCode(code = 200, condition = "Operation successful"),\r
75             @ResponseCode(code = 401, condition = "Unauthorized"),\r
76             @ResponseCode(code = 501, condition = "Not Implemented") })\r
77     public Response listFloatingIPs(\r
78             // return fields\r
79             @QueryParam("fields") List<String> fields,\r
80             // note: openstack isn't clear about filtering on lists, so we aren't handling them\r
81             @QueryParam("id") String queryID,\r
82             @QueryParam("floating_network_id") String queryFloatingNetworkId,\r
83             @QueryParam("port_id") String queryPortId,\r
84             @QueryParam("fixed_ip_address") String queryFixedIPAddress,\r
85             @QueryParam("floating_ip_address") String queryFloatingIPAddress,\r
86             @QueryParam("tenant_id") String queryTenantID,\r
87             // pagination\r
88             @QueryParam("limit") String limit,\r
89             @QueryParam("marker") String marker,\r
90             @QueryParam("page_reverse") String pageReverse\r
91             // sorting not supported\r
92             ) {\r
93         INeutronFloatingIPCRUD floatingIPInterface = NeutronNBInterfaces.getIfNBFloatingIPCRUD("default",this);\r
94         if (floatingIPInterface == null) {\r
95             throw new ServiceUnavailableException("Floating IP CRUD Interface "\r
96                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
97         }\r
98         List<NeutronFloatingIP> allFloatingIPs = floatingIPInterface.getAllFloatingIPs();\r
99         List<NeutronFloatingIP> ans = new ArrayList<NeutronFloatingIP>();\r
100         Iterator<NeutronFloatingIP> i = allFloatingIPs.iterator();\r
101         while (i.hasNext()) {\r
102             NeutronFloatingIP oSS = i.next();\r
103             //match filters: TODO provider extension and router extension\r
104             if ((queryID == null || queryID.equals(oSS.getID())) &&\r
105                     (queryFloatingNetworkId == null || queryFloatingNetworkId.equals(oSS.getFloatingNetworkUUID())) &&\r
106                     (queryPortId == null || queryPortId.equals(oSS.getPortUUID())) &&\r
107                     (queryFixedIPAddress == null || queryFixedIPAddress.equals(oSS.getFixedIPAddress())) &&\r
108                     (queryFloatingIPAddress == null || queryFloatingIPAddress.equals(oSS.getFloatingIPAddress())) &&\r
109                     (queryTenantID == null || queryTenantID.equals(oSS.getTenantUUID()))) {\r
110                 if (fields.size() > 0)\r
111                     ans.add(extractFields(oSS,fields));\r
112                 else\r
113                     ans.add(oSS);\r
114             }\r
115         }\r
116         //TODO: apply pagination to results\r
117         return Response.status(200).entity(\r
118                 new NeutronFloatingIPRequest(ans)).build();\r
119     }\r
120 \r
121     /**\r
122      * Returns a specific FloatingIP */\r
123 \r
124     @Path("{floatingipUUID}")\r
125     @GET\r
126     @Produces({ MediaType.APPLICATION_JSON })\r
127     @StatusCodes({\r
128             @ResponseCode(code = 200, condition = "Operation successful"),\r
129             @ResponseCode(code = 401, condition = "Unauthorized"),\r
130             @ResponseCode(code = 404, condition = "Not Found"),\r
131             @ResponseCode(code = 501, condition = "Not Implemented") })\r
132     public Response showFloatingIP(\r
133             @PathParam("floatingipUUID") String floatingipUUID,\r
134             // return fields\r
135             @QueryParam("fields") List<String> fields ) {\r
136         INeutronFloatingIPCRUD floatingIPInterface = NeutronNBInterfaces.getIfNBFloatingIPCRUD("default",this);\r
137         if (floatingIPInterface == null) {\r
138             throw new ServiceUnavailableException("Floating IP CRUD Interface "\r
139                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
140         }\r
141         if (!floatingIPInterface.floatingIPExists(floatingipUUID))\r
142             return Response.status(404).build();\r
143         if (fields.size() > 0) {\r
144             NeutronFloatingIP ans = floatingIPInterface.getFloatingIP(floatingipUUID);\r
145             return Response.status(200).entity(\r
146                     new NeutronFloatingIPRequest(extractFields(ans, fields))).build();\r
147         } else\r
148             return Response.status(200).entity(\r
149                     new NeutronFloatingIPRequest(floatingIPInterface.getFloatingIP(floatingipUUID))).build();\r
150 \r
151     }\r
152 \r
153     /**\r
154      * Creates new FloatingIPs */\r
155 \r
156     @POST\r
157     @Produces({ MediaType.APPLICATION_JSON })\r
158     @Consumes({ MediaType.APPLICATION_JSON })\r
159     @StatusCodes({\r
160         @ResponseCode(code = 201, condition = "Created"),\r
161         @ResponseCode(code = 400, condition = "Bad Request"),\r
162         @ResponseCode(code = 401, condition = "Unauthorized"),\r
163         @ResponseCode(code = 409, condition = "Conflict"),\r
164         @ResponseCode(code = 501, condition = "Not Implemented") })\r
165     public Response createFloatingIPs(final NeutronFloatingIPRequest input) {\r
166         INeutronFloatingIPCRUD floatingIPInterface = NeutronNBInterfaces.getIfNBFloatingIPCRUD("default",this);\r
167         if (floatingIPInterface == null) {\r
168             throw new ServiceUnavailableException("Floating IP CRUD Interface "\r
169                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
170         }\r
171         INeutronNetworkCRUD networkInterface = NeutronNBInterfaces.getIfNBNetworkCRUD("default", this);\r
172         if (networkInterface == null) {\r
173             throw new ServiceUnavailableException("Network CRUD Interface "\r
174                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
175         }\r
176         INeutronSubnetCRUD subnetInterface = NeutronNBInterfaces.getIfNBSubnetCRUD("default", this);\r
177         if (subnetInterface == null) {\r
178             throw new ServiceUnavailableException("Subnet CRUD Interface "\r
179                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
180         }\r
181         INeutronPortCRUD portInterface = NeutronNBInterfaces.getIfNBPortCRUD("default", this);\r
182         if (portInterface == null) {\r
183             throw new ServiceUnavailableException("Port CRUD Interface "\r
184                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
185         }\r
186         if (input.isSingleton()) {\r
187             NeutronFloatingIP singleton = input.getSingleton();\r
188             // check existence of id in cache and return badrequest if exists\r
189             if (floatingIPInterface.floatingIPExists(singleton.getID()))\r
190                 return Response.status(400).build();\r
191             // check if the external network is specified, exists, and is an external network\r
192             String externalNetworkUUID = singleton.getFloatingNetworkUUID();\r
193             if (externalNetworkUUID == null)\r
194                 return Response.status(400).build();\r
195             if (!networkInterface.networkExists(externalNetworkUUID))\r
196                 return Response.status(400).build();\r
197             NeutronNetwork externNetwork = networkInterface.getNetwork(externalNetworkUUID);\r
198             if (!externNetwork.isRouterExternal())\r
199                 return Response.status(400).build();\r
200             // if floating IP is specified, make sure it can come from the network\r
201             String floatingIP = singleton.getFloatingIPAddress();\r
202             if (floatingIP != null) {\r
203                 if (externNetwork.getSubnets().size() > 1)\r
204                     return Response.status(400).build();\r
205                 NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));\r
206                 if (!externSubnet.isValidIP(floatingIP))\r
207                     return Response.status(400).build();\r
208                 if (externSubnet.isIPInUse(floatingIP))\r
209                     return Response.status(409).build();\r
210             }\r
211             // if port_id is specified, then check that the port exists and has at least one IP\r
212             String port_id = singleton.getPortUUID();\r
213             if (port_id != null) {\r
214                 String fixedIP = null;        // used for the fixedIP calculation\r
215                 if (!portInterface.portExists(port_id))\r
216                     return Response.status(404).build();\r
217                 NeutronPort port = portInterface.getPort(port_id);\r
218                 if (port.getFixedIPs().size() < 1)\r
219                     return Response.status(400).build();\r
220                 // if there is more than one fixed IP then check for fixed_ip_address\r
221                 // and that it is in the list of port addresses\r
222                 if (port.getFixedIPs().size() > 1) {\r
223                     fixedIP = singleton.getFixedIPAddress();\r
224                     if (fixedIP == null)\r
225                         return Response.status(400).build();\r
226                     Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();\r
227                     boolean validFixedIP = false;\r
228                     while (i.hasNext() && !validFixedIP) {\r
229                         Neutron_IPs ip = i.next();\r
230                         if (ip.getIpAddress().equals(fixedIP))\r
231                             validFixedIP = true;\r
232                     }\r
233                     if (!validFixedIP)\r
234                         return Response.status(400).build();\r
235                 } else {\r
236                     fixedIP = port.getFixedIPs().get(0).getIpAddress();\r
237                     if (singleton.getFixedIPAddress() != null && !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))\r
238                         return Response.status(400).build();\r
239                 }\r
240                 //lastly check that this fixed IP address isn't already used\r
241                 if (port.isBoundToFloatingIP(fixedIP))\r
242                     return Response.status(409).build();\r
243                 singleton.setFixedIPAddress(fixedIP);\r
244             }\r
245             Object[] instances = ServiceHelper.getGlobalInstances(INeutronFloatingIPAware.class, this, null);\r
246             if (instances != null) {\r
247                 for (Object instance : instances) {\r
248                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
249                     int status = service.canCreateFloatingIP(singleton);\r
250                     if (status < 200 || status > 299)\r
251                         return Response.status(status).build();\r
252                 }\r
253             }\r
254             floatingIPInterface.addFloatingIP(singleton);\r
255             if (instances != null) {\r
256                 for (Object instance : instances) {\r
257                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
258                     service.neutronFloatingIPCreated(singleton);\r
259                 }\r
260             }\r
261         } else {\r
262             return Response.status(400).build();\r
263         }\r
264         return Response.status(201).entity(input).build();\r
265     }\r
266 \r
267     /**\r
268      * Updates a FloatingIP */\r
269 \r
270     @Path("{floatingipUUID}")\r
271     @PUT\r
272     @Produces({ MediaType.APPLICATION_JSON })\r
273     @Consumes({ MediaType.APPLICATION_JSON })\r
274     @StatusCodes({\r
275             @ResponseCode(code = 200, condition = "Operation successful"),\r
276             @ResponseCode(code = 400, condition = "Bad Request"),\r
277             @ResponseCode(code = 401, condition = "Unauthorized"),\r
278             @ResponseCode(code = 404, condition = "Not Found"),\r
279             @ResponseCode(code = 409, condition = "Conflict"),\r
280             @ResponseCode(code = 501, condition = "Not Implemented") })\r
281     public Response updateFloatingIP(\r
282             @PathParam("floatingipUUID") String floatingipUUID,\r
283             NeutronFloatingIPRequest input\r
284             ) {\r
285         INeutronFloatingIPCRUD floatingIPInterface = NeutronNBInterfaces.getIfNBFloatingIPCRUD("default",this);\r
286         if (floatingIPInterface == null) {\r
287             throw new ServiceUnavailableException("Floating IP CRUD Interface "\r
288                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
289         }\r
290         INeutronNetworkCRUD networkInterface = NeutronNBInterfaces.getIfNBNetworkCRUD("default", this);\r
291         if (networkInterface == null) {\r
292             throw new ServiceUnavailableException("Network CRUD Interface "\r
293                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
294         }\r
295         INeutronSubnetCRUD subnetInterface = NeutronNBInterfaces.getIfNBSubnetCRUD("default", this);\r
296         if (subnetInterface == null) {\r
297             throw new ServiceUnavailableException("Subnet CRUD Interface "\r
298                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
299         }\r
300         INeutronPortCRUD portInterface = NeutronNBInterfaces.getIfNBPortCRUD("default", this);\r
301         if (portInterface == null) {\r
302             throw new ServiceUnavailableException("Port CRUD Interface "\r
303                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
304         }\r
305         if (!floatingIPInterface.floatingIPExists(floatingipUUID))\r
306             return Response.status(404).build();\r
307 \r
308         NeutronFloatingIP sourceFloatingIP = floatingIPInterface.getFloatingIP(floatingipUUID);\r
309         if (!input.isSingleton())\r
310             return Response.status(400).build();\r
311         NeutronFloatingIP singleton = input.getSingleton();\r
312         if (singleton.getID() != null)\r
313             return Response.status(400).build();\r
314 \r
315         NeutronNetwork externNetwork = networkInterface.getNetwork(\r
316                 sourceFloatingIP.getFloatingNetworkUUID());\r
317 \r
318         // if floating IP is specified, make sure it can come from the network\r
319         String floatingIP = singleton.getFloatingIPAddress();\r
320         if (floatingIP != null) {\r
321             if (externNetwork.getSubnets().size() > 1)\r
322                 return Response.status(400).build();\r
323             NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));\r
324             if (!externSubnet.isValidIP(floatingIP))\r
325                 return Response.status(400).build();\r
326             if (externSubnet.isIPInUse(floatingIP))\r
327                 return Response.status(409).build();\r
328         }\r
329 \r
330         // if port_id is specified, then check that the port exists and has at least one IP\r
331         String port_id = singleton.getPortUUID();\r
332         if (port_id != null) {\r
333             String fixedIP = null;        // used for the fixedIP calculation\r
334             if (!portInterface.portExists(port_id))\r
335                 return Response.status(404).build();\r
336             NeutronPort port = portInterface.getPort(port_id);\r
337             if (port.getFixedIPs().size() < 1)\r
338                 return Response.status(400).build();\r
339             // if there is more than one fixed IP then check for fixed_ip_address\r
340             // and that it is in the list of port addresses\r
341             if (port.getFixedIPs().size() > 1) {\r
342                 fixedIP = singleton.getFixedIPAddress();\r
343                 if (fixedIP == null)\r
344                     return Response.status(400).build();\r
345                 Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();\r
346                 boolean validFixedIP = false;\r
347                 while (i.hasNext() && !validFixedIP) {\r
348                     Neutron_IPs ip = i.next();\r
349                     if (ip.getIpAddress().equals(fixedIP))\r
350                         validFixedIP = true;\r
351                 }\r
352                 if (!validFixedIP)\r
353                     return Response.status(400).build();\r
354             } else {\r
355                 fixedIP = port.getFixedIPs().get(0).getIpAddress();\r
356                 if (singleton.getFixedIPAddress() != null &&\r
357                         !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))\r
358                     return Response.status(400).build();\r
359             }\r
360             //lastly check that this fixed IP address isn't already used\r
361             if (port.isBoundToFloatingIP(fixedIP))\r
362                 return Response.status(409).build();\r
363             singleton.setFixedIPAddress(fixedIP);\r
364         }\r
365         NeutronFloatingIP target = floatingIPInterface.getFloatingIP(floatingipUUID);\r
366         Object[] instances = ServiceHelper.getGlobalInstances(INeutronFloatingIPAware.class, this, null);\r
367         if (instances != null) {\r
368             for (Object instance : instances) {\r
369                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
370                 int status = service.canUpdateFloatingIP(singleton, target);\r
371                 if (status < 200 || status > 299)\r
372                     return Response.status(status).build();\r
373             }\r
374         }\r
375         floatingIPInterface.updateFloatingIP(floatingipUUID, singleton);\r
376         target = floatingIPInterface.getFloatingIP(floatingipUUID);\r
377         if (instances != null) {\r
378             for (Object instance : instances) {\r
379                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
380                 service.neutronFloatingIPUpdated(target);\r
381             }\r
382         }\r
383         return Response.status(200).entity(\r
384                 new NeutronFloatingIPRequest(target)).build();\r
385 \r
386     }\r
387 \r
388     /**\r
389      * Deletes a FloatingIP */\r
390 \r
391     @Path("{floatingipUUID}")\r
392     @DELETE\r
393     @StatusCodes({\r
394             @ResponseCode(code = 204, condition = "No Content"),\r
395             @ResponseCode(code = 401, condition = "Unauthorized"),\r
396             @ResponseCode(code = 404, condition = "Not Found"),\r
397             @ResponseCode(code = 501, condition = "Not Implemented") })\r
398     public Response deleteFloatingIP(\r
399             @PathParam("floatingipUUID") String floatingipUUID) {\r
400         INeutronFloatingIPCRUD floatingIPInterface = NeutronNBInterfaces.getIfNBFloatingIPCRUD("default",this);\r
401         if (floatingIPInterface == null) {\r
402             throw new ServiceUnavailableException("Floating IP CRUD Interface "\r
403                     + RestMessages.SERVICEUNAVAILABLE.toString());\r
404         }\r
405         if (!floatingIPInterface.floatingIPExists(floatingipUUID))\r
406             return Response.status(404).build();\r
407         // TODO: need to undo port association if it exists\r
408         NeutronFloatingIP singleton = floatingIPInterface.getFloatingIP(floatingipUUID);\r
409         Object[] instances = ServiceHelper.getGlobalInstances(INeutronFloatingIPAware.class, this, null);\r
410         if (instances != null) {\r
411             for (Object instance : instances) {\r
412                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
413                 int status = service.canDeleteFloatingIP(singleton);\r
414                 if (status < 200 || status > 299)\r
415                     return Response.status(status).build();\r
416             }\r
417         }\r
418         floatingIPInterface.removeFloatingIP(floatingipUUID);\r
419         if (instances != null) {\r
420             for (Object instance : instances) {\r
421                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;\r
422                 service.neutronFloatingIPDeleted(singleton);\r
423             }\r
424         }\r
425         return Response.status(204).build();\r
426     }\r
427 }\r