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