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