Bring features/neutron into the same parent dir as everything else
[controller.git] / opendaylight / networkconfiguration / neutron / northbound / 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
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.controller.networkconfig.neutron.INeutronFloatingIPAware;
30 import org.opendaylight.controller.networkconfig.neutron.INeutronFloatingIPCRUD;
31 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
32 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
33 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
34 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
35 import org.opendaylight.controller.networkconfig.neutron.NeutronFloatingIP;
36 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
37 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
38 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
39 import org.opendaylight.controller.networkconfig.neutron.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     public Response listFloatingIPs(
76             // return fields
77             @QueryParam("fields") List<String> fields,
78             // note: openstack isn't clear about filtering on lists, so we aren't handling them
79             @QueryParam("id") String queryID,
80             @QueryParam("floating_network_id") String queryFloatingNetworkId,
81             @QueryParam("port_id") String queryPortId,
82             @QueryParam("fixed_ip_address") String queryFixedIPAddress,
83             @QueryParam("floating_ip_address") String queryFloatingIPAddress,
84             @QueryParam("tenant_id") String queryTenantID,
85             // pagination
86             @QueryParam("limit") String limit,
87             @QueryParam("marker") String marker,
88             @QueryParam("page_reverse") String pageReverse
89             // sorting not supported
90             ) {
91         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
92         if (floatingIPInterface == null) {
93             throw new ServiceUnavailableException("Floating IP CRUD Interface "
94                     + RestMessages.SERVICEUNAVAILABLE.toString());
95         }
96         List<NeutronFloatingIP> allFloatingIPs = floatingIPInterface.getAllFloatingIPs();
97         List<NeutronFloatingIP> ans = new ArrayList<NeutronFloatingIP>();
98         Iterator<NeutronFloatingIP> i = allFloatingIPs.iterator();
99         while (i.hasNext()) {
100             NeutronFloatingIP oSS = i.next();
101             //match filters: TODO provider extension and router extension
102             if ((queryID == null || queryID.equals(oSS.getID())) &&
103                     (queryFloatingNetworkId == null || queryFloatingNetworkId.equals(oSS.getFloatingNetworkUUID())) &&
104                     (queryPortId == null || queryPortId.equals(oSS.getPortUUID())) &&
105                     (queryFixedIPAddress == null || queryFixedIPAddress.equals(oSS.getFixedIPAddress())) &&
106                     (queryFloatingIPAddress == null || queryFloatingIPAddress.equals(oSS.getFloatingIPAddress())) &&
107                     (queryTenantID == null || queryTenantID.equals(oSS.getTenantUUID()))) {
108                 if (fields.size() > 0)
109                     ans.add(extractFields(oSS,fields));
110                 else
111                     ans.add(oSS);
112             }
113         }
114         //TODO: apply pagination to results
115         return Response.status(200).entity(
116                 new NeutronFloatingIPRequest(ans)).build();
117     }
118
119     /**
120      * Returns a specific FloatingIP */
121
122     @Path("{floatingipUUID}")
123     @GET
124     @Produces({ MediaType.APPLICATION_JSON })
125     @StatusCodes({
126             @ResponseCode(code = 200, condition = "Operation successful"),
127             @ResponseCode(code = 401, condition = "Unauthorized"),
128             @ResponseCode(code = 404, condition = "Not Found"),
129             @ResponseCode(code = 501, condition = "Not Implemented") })
130     public Response showFloatingIP(
131             @PathParam("floatingipUUID") String floatingipUUID,
132             // return fields
133             @QueryParam("fields") List<String> fields ) {
134         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
135         if (floatingIPInterface == null) {
136             throw new ServiceUnavailableException("Floating IP CRUD Interface "
137                     + RestMessages.SERVICEUNAVAILABLE.toString());
138         }
139         if (!floatingIPInterface.floatingIPExists(floatingipUUID))
140             throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
141         if (fields.size() > 0) {
142             NeutronFloatingIP ans = floatingIPInterface.getFloatingIP(floatingipUUID);
143             return Response.status(200).entity(
144                     new NeutronFloatingIPRequest(extractFields(ans, fields))).build();
145         } else
146             return Response.status(200).entity(
147                     new NeutronFloatingIPRequest(floatingIPInterface.getFloatingIP(floatingipUUID))).build();
148
149     }
150
151     /**
152      * Creates new FloatingIPs */
153
154     @POST
155     @Produces({ MediaType.APPLICATION_JSON })
156     @Consumes({ MediaType.APPLICATION_JSON })
157     @StatusCodes({
158         @ResponseCode(code = 201, condition = "Created"),
159         @ResponseCode(code = 400, condition = "Bad Request"),
160         @ResponseCode(code = 401, condition = "Unauthorized"),
161         @ResponseCode(code = 409, condition = "Conflict"),
162         @ResponseCode(code = 501, condition = "Not Implemented") })
163     public Response createFloatingIPs(final NeutronFloatingIPRequest input) {
164         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
165         if (floatingIPInterface == null) {
166             throw new ServiceUnavailableException("Floating IP CRUD Interface "
167                     + RestMessages.SERVICEUNAVAILABLE.toString());
168         }
169         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
170         if (networkInterface == null) {
171             throw new ServiceUnavailableException("Network CRUD Interface "
172                     + RestMessages.SERVICEUNAVAILABLE.toString());
173         }
174         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
175         if (subnetInterface == null) {
176             throw new ServiceUnavailableException("Subnet CRUD Interface "
177                     + RestMessages.SERVICEUNAVAILABLE.toString());
178         }
179         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD( this);
180         if (portInterface == null) {
181             throw new ServiceUnavailableException("Port CRUD Interface "
182                     + RestMessages.SERVICEUNAVAILABLE.toString());
183         }
184         if (input.isSingleton()) {
185             NeutronFloatingIP singleton = input.getSingleton();
186             // check existence of id in cache and return badrequest if exists
187             if (floatingIPInterface.floatingIPExists(singleton.getID()))
188                 throw new BadRequestException("Floating IP UUID already exists.");
189             // check if the external network is specified, exists, and is an external network
190             String externalNetworkUUID = singleton.getFloatingNetworkUUID();
191             if (externalNetworkUUID == null)
192                 throw new BadRequestException("external network UUID doesn't exist.");
193             if (!networkInterface.networkExists(externalNetworkUUID))
194                 throw new BadRequestException("external network UUID doesn't exist.");
195             NeutronNetwork externNetwork = networkInterface.getNetwork(externalNetworkUUID);
196             if (!externNetwork.isRouterExternal())
197                 throw new BadRequestException("external network isn't marked router:external");
198             // if floating IP is specified, make sure it can come from the network
199             String floatingIP = singleton.getFloatingIPAddress();
200             if (floatingIP != null) {
201                 if (externNetwork.getSubnets().size() != 1)
202                     throw new BadRequestException("external network doesn't have a subnet");
203                 NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
204                 if (!externSubnet.isValidIP(floatingIP))
205                     throw new BadRequestException("external IP isn't valid for the specified subnet.");
206                 if (externSubnet.isIPInUse(floatingIP))
207                     throw new ResourceConflictException("floating IP is in use.");
208             }
209             // if port_id is specified, then check that the port exists and has at least one IP
210             String port_id = singleton.getPortUUID();
211             if (port_id != null) {
212                 String fixedIP = null;        // used for the fixedIP calculation
213                 if (!portInterface.portExists(port_id))
214                     throw new ResourceNotFoundException("Port UUID doesn't exist.");
215                 NeutronPort port = portInterface.getPort(port_id);
216                 if (port.getFixedIPs().size() < 1)
217                     throw new BadRequestException("port UUID doesn't have an IP address.");
218                 // if there is more than one fixed IP then check for fixed_ip_address
219                 // and that it is in the list of port addresses
220                 if (port.getFixedIPs().size() > 1) {
221                     fixedIP = singleton.getFixedIPAddress();
222                     if (fixedIP == null)
223                         throw new BadRequestException("fixed IP address doesn't exist.");
224                     Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();
225                     boolean validFixedIP = false;
226                     while (i.hasNext() && !validFixedIP) {
227                         Neutron_IPs ip = i.next();
228                         if (ip.getIpAddress().equals(fixedIP))
229                             validFixedIP = true;
230                     }
231                     if (!validFixedIP)
232                         throw new BadRequestException("can't find a valid fixed IP address");
233                 } else {
234                     fixedIP = port.getFixedIPs().get(0).getIpAddress();
235                     if (singleton.getFixedIPAddress() != null && !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))
236                         throw new BadRequestException("mismatched fixed IP address in request");
237                 }
238                 //lastly check that this fixed IP address isn't already used
239                 if (port.isBoundToFloatingIP(fixedIP))
240                     throw new ResourceConflictException("fixed IP is in use.");
241                 singleton.setFixedIPAddress(fixedIP);
242             }
243             Object[] instances = NeutronUtil.getInstances(INeutronFloatingIPAware.class, this);
244             if (instances != null) {
245                 for (Object instance : instances) {
246                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
247                     int status = service.canCreateFloatingIP(singleton);
248                     if (status < 200 || status > 299)
249                         return Response.status(status).build();
250                 }
251             }
252             floatingIPInterface.addFloatingIP(singleton);
253             if (instances != null) {
254                 for (Object instance : instances) {
255                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
256                     service.neutronFloatingIPCreated(singleton);
257                 }
258             }
259         } else {
260             throw new BadRequestException("only singleton requests allowed.");
261         }
262         return Response.status(201).entity(input).build();
263     }
264
265     /**
266      * Updates a FloatingIP */
267
268     @Path("{floatingipUUID}")
269     @PUT
270     @Produces({ MediaType.APPLICATION_JSON })
271     @Consumes({ MediaType.APPLICATION_JSON })
272     @StatusCodes({
273             @ResponseCode(code = 200, condition = "Operation successful"),
274             @ResponseCode(code = 400, condition = "Bad Request"),
275             @ResponseCode(code = 401, condition = "Unauthorized"),
276             @ResponseCode(code = 404, condition = "Not Found"),
277             @ResponseCode(code = 409, condition = "Conflict"),
278             @ResponseCode(code = 501, condition = "Not Implemented") })
279     public Response updateFloatingIP(
280             @PathParam("floatingipUUID") String floatingipUUID,
281             NeutronFloatingIPRequest input
282             ) {
283         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
284         if (floatingIPInterface == null) {
285             throw new ServiceUnavailableException("Floating IP CRUD Interface "
286                     + RestMessages.SERVICEUNAVAILABLE.toString());
287         }
288         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
289         if (networkInterface == null) {
290             throw new ServiceUnavailableException("Network CRUD Interface "
291                     + RestMessages.SERVICEUNAVAILABLE.toString());
292         }
293         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
294         if (subnetInterface == null) {
295             throw new ServiceUnavailableException("Subnet CRUD Interface "
296                     + RestMessages.SERVICEUNAVAILABLE.toString());
297         }
298         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD( this);
299         if (portInterface == null) {
300             throw new ServiceUnavailableException("Port CRUD Interface "
301                     + RestMessages.SERVICEUNAVAILABLE.toString());
302         }
303         if (!floatingIPInterface.floatingIPExists(floatingipUUID))
304             throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
305
306         NeutronFloatingIP sourceFloatingIP = floatingIPInterface.getFloatingIP(floatingipUUID);
307         if (!input.isSingleton())
308             throw new BadRequestException("only singleton requests allowed.");
309         NeutronFloatingIP singleton = input.getSingleton();
310         if (singleton.getID() == null)
311             throw new BadRequestException("singleton UUID doesn't exist.");
312
313         NeutronNetwork externNetwork = networkInterface.getNetwork(
314                 sourceFloatingIP.getFloatingNetworkUUID());
315
316         // if floating IP is specified, make sure it can come from the network
317         String floatingIP = singleton.getFloatingIPAddress();
318         if (floatingIP != null) {
319             if (externNetwork.getSubnets().size() != 1)
320                 throw new BadRequestException("external network doesn't have a subnet.");
321             NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
322             if (!externSubnet.isValidIP(floatingIP))
323                 throw new BadRequestException("floating IP not valid for external subnet");
324             if (externSubnet.isIPInUse(floatingIP))
325                 throw new ResourceConflictException("floating IP is in use.");
326         }
327
328         // if port_id is specified, then check that the port exists and has at least one IP
329         String port_id = singleton.getPortUUID();
330         if (port_id != null) {
331             String fixedIP = null;        // used for the fixedIP calculation
332             if (!portInterface.portExists(port_id))
333                 throw new ResourceNotFoundException("Port UUID doesn't exist.");
334             NeutronPort port = portInterface.getPort(port_id);
335             if (port.getFixedIPs().size() < 1)
336                 throw new BadRequestException("port ID doesn't have a fixed IP address.");
337             // if there is more than one fixed IP then check for fixed_ip_address
338             // and that it is in the list of port addresses
339             if (port.getFixedIPs().size() > 1) {
340                 fixedIP = singleton.getFixedIPAddress();
341                 if (fixedIP == null)
342                     throw new BadRequestException("request doesn't have a fixed IP address");
343                 Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();
344                 boolean validFixedIP = false;
345                 while (i.hasNext() && !validFixedIP) {
346                     Neutron_IPs ip = i.next();
347                     if (ip.getIpAddress().equals(fixedIP))
348                         validFixedIP = true;
349                 }
350                 if (!validFixedIP)
351                     throw new BadRequestException("couldn't find a valid fixed IP address");
352             } else {
353                 fixedIP = port.getFixedIPs().get(0).getIpAddress();
354                 if (singleton.getFixedIPAddress() != null &&
355                         !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))
356                     throw new BadRequestException("mismatch in fixed IP addresses");
357             }
358             //lastly check that this fixed IP address isn't already used
359             if (port.isBoundToFloatingIP(fixedIP))
360                 throw new ResourceConflictException("fixed IP is in use.");
361             singleton.setFixedIPAddress(fixedIP);
362         }
363         NeutronFloatingIP target = floatingIPInterface.getFloatingIP(floatingipUUID);
364         Object[] instances = NeutronUtil.getInstances(INeutronFloatingIPAware.class, this);
365         if (instances != null) {
366             for (Object instance : instances) {
367                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
368                 int status = service.canUpdateFloatingIP(singleton, target);
369                 if (status < 200 || status > 299)
370                     return Response.status(status).build();
371             }
372         }
373         floatingIPInterface.updateFloatingIP(floatingipUUID, singleton);
374         target = floatingIPInterface.getFloatingIP(floatingipUUID);
375         if (instances != null) {
376             for (Object instance : instances) {
377                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
378                 service.neutronFloatingIPUpdated(target);
379             }
380         }
381         return Response.status(200).entity(
382                 new NeutronFloatingIPRequest(target)).build();
383
384     }
385
386     /**
387      * Deletes a FloatingIP */
388
389     @Path("{floatingipUUID}")
390     @DELETE
391     @StatusCodes({
392             @ResponseCode(code = 204, condition = "No Content"),
393             @ResponseCode(code = 401, condition = "Unauthorized"),
394             @ResponseCode(code = 404, condition = "Not Found"),
395             @ResponseCode(code = 501, condition = "Not Implemented") })
396     public Response deleteFloatingIP(
397             @PathParam("floatingipUUID") String floatingipUUID) {
398         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
399         if (floatingIPInterface == null) {
400             throw new ServiceUnavailableException("Floating IP CRUD Interface "
401                     + RestMessages.SERVICEUNAVAILABLE.toString());
402         }
403         if (!floatingIPInterface.floatingIPExists(floatingipUUID))
404             throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
405         // TODO: need to undo port association if it exists
406         NeutronFloatingIP singleton = floatingIPInterface.getFloatingIP(floatingipUUID);
407         Object[] instances = NeutronUtil.getInstances(INeutronFloatingIPAware.class, this);
408         if (instances != null) {
409             for (Object instance : instances) {
410                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
411                 int status = service.canDeleteFloatingIP(singleton);
412                 if (status < 200 || status > 299)
413                     return Response.status(status).build();
414             }
415         }
416         floatingIPInterface.removeFloatingIP(floatingipUUID);
417         if (instances != null) {
418             for (Object instance : instances) {
419                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
420                 service.neutronFloatingIPDeleted(singleton);
421             }
422         }
423         return Response.status(204).build();
424     }
425 }