2 * Copyright IBM Corporation, 2013. All rights reserved.
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
9 package org.opendaylight.controller.networkconfig.neutron.northbound;
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;
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;
47 * Neutron Northbound REST APIs.<br>
48 * This class provides REST APIs for managing Neutron Floating IPs
52 * Authentication scheme : <b>HTTP Basic</b><br>
53 * Authentication realm : <b>opendaylight</b><br>
54 * Transport : <b>HTTP and HTTPS</b><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>
60 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
65 public class NeutronFloatingIPsNorthbound {
67 private NeutronFloatingIP extractFields(NeutronFloatingIP o, List<String> fields) {
68 return o.extractFields(fields);
72 * Returns a list of all FloatingIPs */
75 @Produces({ MediaType.APPLICATION_JSON })
77 @ResponseCode(code = 200, condition = "Operation successful"),
78 @ResponseCode(code = 401, condition = "Unauthorized"),
79 @ResponseCode(code = 501, condition = "Not Implemented") })
80 public Response listFloatingIPs(
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,
91 @QueryParam("limit") String limit,
92 @QueryParam("marker") String marker,
93 @QueryParam("page_reverse") String pageReverse
94 // sorting not supported
96 INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
97 if (floatingIPInterface == null) {
98 throw new ServiceUnavailableException("Floating IP CRUD Interface "
99 + RestMessages.SERVICEUNAVAILABLE.toString());
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));
119 //TODO: apply pagination to results
120 return Response.status(200).entity(
121 new NeutronFloatingIPRequest(ans)).build();
125 * Returns a specific FloatingIP */
127 @Path("{floatingipUUID}")
129 @Produces({ MediaType.APPLICATION_JSON })
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,
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());
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();
151 return Response.status(200).entity(
152 new NeutronFloatingIPRequest(floatingIPInterface.getFloatingIP(floatingipUUID))).build();
157 * Creates new FloatingIPs */
160 @Produces({ MediaType.APPLICATION_JSON })
161 @Consumes({ MediaType.APPLICATION_JSON })
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());
174 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
175 if (networkInterface == null) {
176 throw new ServiceUnavailableException("Network CRUD Interface "
177 + RestMessages.SERVICEUNAVAILABLE.toString());
179 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
180 if (subnetInterface == null) {
181 throw new ServiceUnavailableException("Subnet CRUD Interface "
182 + RestMessages.SERVICEUNAVAILABLE.toString());
184 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD( this);
185 if (portInterface == null) {
186 throw new ServiceUnavailableException("Port CRUD Interface "
187 + RestMessages.SERVICEUNAVAILABLE.toString());
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.");
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();
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))
237 throw new BadRequestException("can't find a valid fixed IP address");
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");
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);
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();
257 floatingIPInterface.addFloatingIP(singleton);
258 if (instances != null) {
259 for (Object instance : instances) {
260 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
261 service.neutronFloatingIPCreated(singleton);
265 throw new BadRequestException("only singleton requests allowed.");
267 return Response.status(201).entity(input).build();
271 * Updates a FloatingIP */
273 @Path("{floatingipUUID}")
275 @Produces({ MediaType.APPLICATION_JSON })
276 @Consumes({ MediaType.APPLICATION_JSON })
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
288 INeutronFloatingIPCRUD floatingIPInterface = NeutronCRUDInterfaces.getINeutronFloatingIPCRUD(this);
289 if (floatingIPInterface == null) {
290 throw new ServiceUnavailableException("Floating IP CRUD Interface "
291 + RestMessages.SERVICEUNAVAILABLE.toString());
293 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
294 if (networkInterface == null) {
295 throw new ServiceUnavailableException("Network CRUD Interface "
296 + RestMessages.SERVICEUNAVAILABLE.toString());
298 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
299 if (subnetInterface == null) {
300 throw new ServiceUnavailableException("Subnet CRUD Interface "
301 + RestMessages.SERVICEUNAVAILABLE.toString());
303 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD( this);
304 if (portInterface == null) {
305 throw new ServiceUnavailableException("Port CRUD Interface "
306 + RestMessages.SERVICEUNAVAILABLE.toString());
308 if (!floatingIPInterface.floatingIPExists(floatingipUUID))
309 throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
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.");
318 NeutronNetwork externNetwork = networkInterface.getNetwork(
319 sourceFloatingIP.getFloatingNetworkUUID());
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.");
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();
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))
356 throw new BadRequestException("couldn't find a valid fixed IP address");
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");
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);
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();
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);
386 return Response.status(200).entity(
387 new NeutronFloatingIPRequest(target)).build();
392 * Deletes a FloatingIP */
394 @Path("{floatingipUUID}")
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());
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();
421 floatingIPInterface.removeFloatingIP(floatingipUUID);
422 if (instances != null) {
423 for (Object instance : instances) {
424 INeutronFloatingIPAware service = (INeutronFloatingIPAware) instance;
425 service.neutronFloatingIPDeleted(singleton);
428 return Response.status(204).build();