Merge "Startup archetype: remove 'Impl' from config subsystem Module name."
[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                 if (instances.length > 0) {
246                     for (Object instance : instances) {
247                         INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
248                         int status = service.canCreateFloatingIP(singleton);
249                         if (status < 200 || status > 299)
250                             return Response.status(status).build();
251                     }
252                 } else {
253                     throw new ServiceUnavailableException("No providers registered.  Please try again later");
254                 }
255             } else {
256                 throw new ServiceUnavailableException("Couldn't get providers list.  Please try again later");
257             }
258             floatingIPInterface.addFloatingIP(singleton);
259             if (instances != null) {
260                 for (Object instance : instances) {
261                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
262                     service.neutronFloatingIPCreated(singleton);
263                 }
264             }
265         } else {
266             throw new BadRequestException("only singleton requests allowed.");
267         }
268         return Response.status(201).entity(input).build();
269     }
270
271     /**
272      * Updates a FloatingIP */
273
274     @Path("{floatingipUUID}")
275     @PUT
276     @Produces({ MediaType.APPLICATION_JSON })
277     @Consumes({ MediaType.APPLICATION_JSON })
278     @StatusCodes({
279             @ResponseCode(code = 200, condition = "Operation successful"),
280             @ResponseCode(code = 400, condition = "Bad Request"),
281             @ResponseCode(code = 401, condition = "Unauthorized"),
282             @ResponseCode(code = 404, condition = "Not Found"),
283             @ResponseCode(code = 409, condition = "Conflict"),
284             @ResponseCode(code = 501, condition = "Not Implemented") })
285     public Response updateFloatingIP(
286             @PathParam("floatingipUUID") String floatingipUUID,
287             NeutronFloatingIPRequest input
288             ) {
289         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
290         if (floatingIPInterface == null) {
291             throw new ServiceUnavailableException("Floating IP CRUD Interface "
292                     + RestMessages.SERVICEUNAVAILABLE.toString());
293         }
294         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
295         if (networkInterface == null) {
296             throw new ServiceUnavailableException("Network CRUD Interface "
297                     + RestMessages.SERVICEUNAVAILABLE.toString());
298         }
299         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
300         if (subnetInterface == null) {
301             throw new ServiceUnavailableException("Subnet CRUD Interface "
302                     + RestMessages.SERVICEUNAVAILABLE.toString());
303         }
304         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD( this);
305         if (portInterface == null) {
306             throw new ServiceUnavailableException("Port CRUD Interface "
307                     + RestMessages.SERVICEUNAVAILABLE.toString());
308         }
309         if (!floatingIPInterface.floatingIPExists(floatingipUUID))
310             throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
311
312         NeutronFloatingIP sourceFloatingIP = floatingIPInterface.getFloatingIP(floatingipUUID);
313         if (!input.isSingleton())
314             throw new BadRequestException("only singleton requests allowed.");
315         NeutronFloatingIP singleton = input.getSingleton();
316         if (singleton.getID() == null)
317             throw new BadRequestException("singleton UUID doesn't exist.");
318
319         NeutronNetwork externNetwork = networkInterface.getNetwork(
320                 sourceFloatingIP.getFloatingNetworkUUID());
321
322         // if floating IP is specified, make sure it can come from the network
323         String floatingIP = singleton.getFloatingIPAddress();
324         if (floatingIP != null) {
325             if (externNetwork.getSubnets().size() != 1)
326                 throw new BadRequestException("external network doesn't have a subnet.");
327             NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
328             if (!externSubnet.isValidIP(floatingIP))
329                 throw new BadRequestException("floating IP not valid for external subnet");
330             if (externSubnet.isIPInUse(floatingIP))
331                 throw new ResourceConflictException("floating IP is in use.");
332         }
333
334         // if port_id is specified, then check that the port exists and has at least one IP
335         String port_id = singleton.getPortUUID();
336         if (port_id != null) {
337             String fixedIP = null;        // used for the fixedIP calculation
338             if (!portInterface.portExists(port_id))
339                 throw new ResourceNotFoundException("Port UUID doesn't exist.");
340             NeutronPort port = portInterface.getPort(port_id);
341             if (port.getFixedIPs().size() < 1)
342                 throw new BadRequestException("port ID doesn't have a fixed IP address.");
343             // if there is more than one fixed IP then check for fixed_ip_address
344             // and that it is in the list of port addresses
345             if (port.getFixedIPs().size() > 1) {
346                 fixedIP = singleton.getFixedIPAddress();
347                 if (fixedIP == null)
348                     throw new BadRequestException("request doesn't have a fixed IP address");
349                 Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();
350                 boolean validFixedIP = false;
351                 while (i.hasNext() && !validFixedIP) {
352                     Neutron_IPs ip = i.next();
353                     if (ip.getIpAddress().equals(fixedIP))
354                         validFixedIP = true;
355                 }
356                 if (!validFixedIP)
357                     throw new BadRequestException("couldn't find a valid fixed IP address");
358             } else {
359                 fixedIP = port.getFixedIPs().get(0).getIpAddress();
360                 if (singleton.getFixedIPAddress() != null &&
361                         !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))
362                     throw new BadRequestException("mismatch in fixed IP addresses");
363             }
364             //lastly check that this fixed IP address isn't already used
365             if (port.isBoundToFloatingIP(fixedIP))
366                 throw new ResourceConflictException("fixed IP is in use.");
367             singleton.setFixedIPAddress(fixedIP);
368         }
369         NeutronFloatingIP target = floatingIPInterface.getFloatingIP(floatingipUUID);
370         Object[] instances = NeutronUtil.getInstances(INeutronFloatingIPAware.class, this);
371         if (instances != null) {
372             if (instances.length > 0) {
373                 for (Object instance : instances) {
374                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
375                     int status = service.canUpdateFloatingIP(singleton, target);
376                     if (status < 200 || status > 299)
377                         return Response.status(status).build();
378                 }
379             } else {
380                 throw new ServiceUnavailableException("No providers registered.  Please try again later");
381             }
382         } else {
383             throw new ServiceUnavailableException("Couldn't get providers list.  Please try again later");
384         }
385         floatingIPInterface.updateFloatingIP(floatingipUUID, singleton);
386         target = floatingIPInterface.getFloatingIP(floatingipUUID);
387         if (instances != null) {
388             for (Object instance : instances) {
389                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
390                 service.neutronFloatingIPUpdated(target);
391             }
392         }
393         return Response.status(200).entity(
394                 new NeutronFloatingIPRequest(target)).build();
395
396     }
397
398     /**
399      * Deletes a FloatingIP */
400
401     @Path("{floatingipUUID}")
402     @DELETE
403     @StatusCodes({
404             @ResponseCode(code = 204, condition = "No Content"),
405             @ResponseCode(code = 401, condition = "Unauthorized"),
406             @ResponseCode(code = 404, condition = "Not Found"),
407             @ResponseCode(code = 501, condition = "Not Implemented") })
408     public Response deleteFloatingIP(
409             @PathParam("floatingipUUID") String floatingipUUID) {
410         INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
411         if (floatingIPInterface == null) {
412             throw new ServiceUnavailableException("Floating IP CRUD Interface "
413                     + RestMessages.SERVICEUNAVAILABLE.toString());
414         }
415         if (!floatingIPInterface.floatingIPExists(floatingipUUID))
416             throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
417         // TODO: need to undo port association if it exists
418         NeutronFloatingIP singleton = floatingIPInterface.getFloatingIP(floatingipUUID);
419         Object[] instances = NeutronUtil.getInstances(INeutronFloatingIPAware.class, this);
420         if (instances != null) {
421             if (instances.length > 0) {
422                 for (Object instance : instances) {
423                     INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
424                     int status = service.canDeleteFloatingIP(singleton);
425                     if (status < 200 || status > 299)
426                         return Response.status(status).build();
427                 }
428             } else {
429                 throw new ServiceUnavailableException("No providers registered.  Please try again later");
430             }
431         } else {
432             throw new ServiceUnavailableException("Couldn't get providers list.  Please try again later");
433         }
434         floatingIPInterface.removeFloatingIP(floatingipUUID);
435         if (instances != null) {
436             for (Object instance : instances) {
437                 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
438                 service.neutronFloatingIPDeleted(singleton);
439             }
440         }
441         return Response.status(204).build();
442     }
443 }