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.neutron.northbound.api;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.List;
16 import javax.ws.rs.Consumes;
17 import javax.ws.rs.DELETE;
18 import javax.ws.rs.DefaultValue;
19 import javax.ws.rs.GET;
20 import javax.ws.rs.POST;
21 import javax.ws.rs.PUT;
22 import javax.ws.rs.Path;
23 import javax.ws.rs.PathParam;
24 import javax.ws.rs.Produces;
25 import javax.ws.rs.QueryParam;
26 import javax.ws.rs.core.Context;
27 import javax.ws.rs.core.MediaType;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.UriInfo;
31 import org.codehaus.enunciate.jaxrs.ResponseCode;
32 import org.codehaus.enunciate.jaxrs.StatusCodes;
33 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
34 import org.opendaylight.neutron.spi.INeutronPortAware;
35 import org.opendaylight.neutron.spi.INeutronPortCRUD;
36 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
37 import org.opendaylight.neutron.spi.NeutronCRUDInterfaces;
38 import org.opendaylight.neutron.spi.NeutronPort;
39 import org.opendaylight.neutron.spi.NeutronSubnet;
40 import org.opendaylight.neutron.spi.Neutron_IPs;
43 * Neutron Northbound REST APIs.<br>
44 * This class provides REST APIs for managing neutron port objects
48 * Authentication scheme : <b>HTTP Basic</b><br>
49 * Authentication realm : <b>opendaylight</b><br>
50 * Transport : <b>HTTP and HTTPS</b><br>
52 * HTTPS Authentication is disabled by default. Administrator can enable it in
53 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
54 * trusted authority.<br>
56 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
61 public class NeutronPortsNorthbound {
63 final String mac_regex="^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$";
65 private NeutronPort extractFields(NeutronPort o, List<String> fields) {
66 return o.extractFields(fields);
73 * Returns a list of all Ports */
76 @Produces({ MediaType.APPLICATION_JSON })
77 //@TypeHint(OpenStackPorts.class)
79 @ResponseCode(code = 200, condition = "Operation successful"),
80 @ResponseCode(code = 401, condition = "Unauthorized"),
81 @ResponseCode(code = 501, condition = "Not Implemented"),
82 @ResponseCode(code = 503, condition = "No providers available") })
83 public Response listPorts(
85 @QueryParam("fields") List<String> fields,
86 // note: openstack isn't clear about filtering on lists, so we aren't handling them
87 @QueryParam("id") String queryID,
88 @QueryParam("network_id") String queryNetworkID,
89 @QueryParam("name") String queryName,
90 @QueryParam("admin_state_up") String queryAdminStateUp,
91 @QueryParam("status") String queryStatus,
92 @QueryParam("mac_address") String queryMACAddress,
93 @QueryParam("device_id") String queryDeviceID,
94 @QueryParam("device_owner") String queryDeviceOwner,
95 @QueryParam("tenant_id") String queryTenantID,
97 @QueryParam("limit") Integer limit,
98 @QueryParam("marker") String marker,
99 @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
100 // sorting not supported
102 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
103 if (portInterface == null) {
104 throw new ServiceUnavailableException("Port CRUD Interface "
105 + RestMessages.SERVICEUNAVAILABLE.toString());
107 List<NeutronPort> allPorts = portInterface.getAllPorts();
108 List<NeutronPort> ans = new ArrayList<NeutronPort>();
109 Iterator<NeutronPort> i = allPorts.iterator();
110 while (i.hasNext()) {
111 NeutronPort oSS = i.next();
112 if ((queryID == null || queryID.equals(oSS.getID())) &&
113 (queryNetworkID == null || queryNetworkID.equals(oSS.getNetworkUUID())) &&
114 (queryName == null || queryName.equals(oSS.getName())) &&
115 (queryAdminStateUp == null || queryAdminStateUp.equals(oSS.getAdminStateUp())) &&
116 (queryStatus == null || queryStatus.equals(oSS.getStatus())) &&
117 (queryMACAddress == null || queryMACAddress.equals(oSS.getMacAddress())) &&
118 (queryDeviceID == null || queryDeviceID.equals(oSS.getDeviceID())) &&
119 (queryDeviceOwner == null || queryDeviceOwner.equals(oSS.getDeviceOwner())) &&
120 (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
121 if (fields.size() > 0) {
122 ans.add(extractFields(oSS,fields));
129 if (limit != null && ans.size() > 1) {
130 // Return a paginated request
131 NeutronPortRequest request = (NeutronPortRequest) PaginatedRequestFactory.createRequest(limit,
132 marker, pageReverse, uriInfo, ans, NeutronPort.class);
133 return Response.status(200).entity(request).build();
136 return Response.status(200).entity(
137 new NeutronPortRequest(ans)).build();
141 * Returns a specific Port */
145 @Produces({ MediaType.APPLICATION_JSON })
146 //@TypeHint(OpenStackPorts.class)
148 @ResponseCode(code = 200, condition = "Operation successful"),
149 @ResponseCode(code = 401, condition = "Unauthorized"),
150 @ResponseCode(code = 404, condition = "Not Found"),
151 @ResponseCode(code = 501, condition = "Not Implemented"),
152 @ResponseCode(code = 503, condition = "No providers available") })
153 public Response showPort(
154 @PathParam("portUUID") String portUUID,
156 @QueryParam("fields") List<String> fields ) {
157 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
158 if (portInterface == null) {
159 throw new ServiceUnavailableException("Port CRUD Interface "
160 + RestMessages.SERVICEUNAVAILABLE.toString());
162 if (!portInterface.portExists(portUUID)) {
163 throw new ResourceNotFoundException("port UUID does not exist.");
165 if (fields.size() > 0) {
166 NeutronPort ans = portInterface.getPort(portUUID);
167 return Response.status(200).entity(
168 new NeutronPortRequest(extractFields(ans, fields))).build();
170 return Response.status(200).entity(
171 new NeutronPortRequest(portInterface.getPort(portUUID))).build();
176 * Creates new Ports */
179 @Produces({ MediaType.APPLICATION_JSON })
180 @Consumes({ MediaType.APPLICATION_JSON })
181 //@TypeHint(OpenStackPorts.class)
183 @ResponseCode(code = 201, condition = "Created"),
184 @ResponseCode(code = 400, condition = "Bad Request"),
185 @ResponseCode(code = 401, condition = "Unauthorized"),
186 @ResponseCode(code = 403, condition = "Forbidden"),
187 @ResponseCode(code = 404, condition = "Not Found"),
188 @ResponseCode(code = 409, condition = "Conflict"),
189 @ResponseCode(code = 501, condition = "Not Implemented"),
190 @ResponseCode(code = 503, condition = "MAC generation failure"),
191 @ResponseCode(code = 503, condition = "No providers available") })
192 public Response createPorts(final NeutronPortRequest input) {
193 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
194 if (portInterface == null) {
195 throw new ServiceUnavailableException("Port CRUD Interface "
196 + RestMessages.SERVICEUNAVAILABLE.toString());
198 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
199 if (networkInterface == null) {
200 throw new ServiceUnavailableException("Network CRUD Interface "
201 + RestMessages.SERVICEUNAVAILABLE.toString());
203 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
204 if (subnetInterface == null) {
205 throw new ServiceUnavailableException("Subnet CRUD Interface "
206 + RestMessages.SERVICEUNAVAILABLE.toString());
208 if (input.isSingleton()) {
209 NeutronPort singleton = input.getSingleton();
212 * the port must be part of an existing network, must not already exist,
213 * have a valid MAC and the MAC not be in use
215 if (singleton.getNetworkUUID() == null) {
216 throw new BadRequestException("network UUID musy be specified");
218 if (portInterface.portExists(singleton.getID())) {
219 throw new BadRequestException("port UUID already exists");
221 if (!networkInterface.networkExists(singleton.getNetworkUUID())) {
222 throw new ResourceNotFoundException("network UUID does not exist.");
224 if (singleton.getMacAddress() == null ||
225 !singleton.getMacAddress().matches(mac_regex)) {
226 throw new BadRequestException("MAC address not properly formatted");
228 if (portInterface.macInUse(singleton.getMacAddress())) {
229 throw new ResourceConflictException("MAC Address is in use.");
232 * if fixed IPs are specified, each one has to have an existing subnet ID
233 * that is in the same scoping network as the port. In addition, if an IP
234 * address is specified it has to be a valid address for the subnet and not
237 List<Neutron_IPs> fixedIPs = singleton.getFixedIPs();
238 if (fixedIPs != null && fixedIPs.size() > 0) {
239 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
240 while (fixedIPIterator.hasNext()) {
241 Neutron_IPs ip = fixedIPIterator.next();
242 if (ip.getSubnetUUID() == null) {
243 throw new BadRequestException("subnet UUID not specified");
245 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
246 if (subnet == null) {
247 throw new BadRequestException("subnet UUID must exist");
249 if (!singleton.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
250 throw new BadRequestException("network UUID must match that of subnet");
252 if (ip.getIpAddress() != null) {
253 if (!subnet.isValidIP(ip.getIpAddress())) {
254 throw new BadRequestException("IP address is not valid");
256 if (subnet.isIPInUse(ip.getIpAddress())) {
257 throw new ResourceConflictException("IP address is in use.");
263 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
264 if (instances != null) {
265 if (instances.length > 0) {
266 for (Object instance : instances) {
267 INeutronPortAware service = (INeutronPortAware) instance;
268 int status = service.canCreatePort(singleton);
269 if (status < 200 || status > 299) {
270 return Response.status(status).build();
274 throw new ServiceUnavailableException("No providers registered. Please try again later");
277 throw new ServiceUnavailableException("Couldn't get providers list. Please try again later");
280 // add the port to the cache
281 portInterface.addPort(singleton);
282 if (instances != null) {
283 for (Object instance : instances) {
284 INeutronPortAware service = (INeutronPortAware) instance;
285 service.neutronPortCreated(singleton);
289 List<NeutronPort> bulk = input.getBulk();
290 Iterator<NeutronPort> i = bulk.iterator();
291 HashMap<String, NeutronPort> testMap = new HashMap<String, NeutronPort>();
292 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
293 while (i.hasNext()) {
294 NeutronPort test = i.next();
297 * the port must be part of an existing network, must not already exist,
298 * have a valid MAC and the MAC not be in use. Further the bulk request
299 * can't already contain a new port with the same UUID
301 if (portInterface.portExists(test.getID())) {
302 throw new BadRequestException("port UUID already exists");
304 if (testMap.containsKey(test.getID())) {
305 throw new BadRequestException("port UUID already exists");
307 for (NeutronPort check : testMap.values()) {
308 if (test.getMacAddress().equalsIgnoreCase(check.getMacAddress())) {
309 throw new ResourceConflictException("MAC address already allocated");
311 for (Neutron_IPs test_fixedIP : test.getFixedIPs()) {
312 for (Neutron_IPs check_fixedIP : check.getFixedIPs()) {
313 if (test_fixedIP.getSubnetUUID().equals(check_fixedIP.getSubnetUUID())) {
314 if (test_fixedIP.getIpAddress().equals(check_fixedIP.getIpAddress())) {
315 throw new ResourceConflictException("IP address already allocated");
321 testMap.put(test.getID(), test);
322 if (!networkInterface.networkExists(test.getNetworkUUID())) {
323 throw new ResourceNotFoundException("network UUID does not exist.");
325 if (!test.getMacAddress().matches(mac_regex)) {
326 throw new BadRequestException("MAC address not properly formatted");
328 if (portInterface.macInUse(test.getMacAddress())) {
329 throw new ResourceConflictException("MAC address in use");
333 * if fixed IPs are specified, each one has to have an existing subnet ID
334 * that is in the same scoping network as the port. In addition, if an IP
335 * address is specified it has to be a valid address for the subnet and not
336 * already in use (or be the gateway IP address of the subnet)
338 List<Neutron_IPs> fixedIPs = test.getFixedIPs();
339 if (fixedIPs != null && fixedIPs.size() > 0) {
340 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
341 while (fixedIPIterator.hasNext()) {
342 Neutron_IPs ip = fixedIPIterator.next();
343 if (ip.getSubnetUUID() == null) {
344 throw new BadRequestException("subnet UUID must be specified");
346 if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
347 throw new BadRequestException("subnet UUID doesn't exists");
349 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
350 if (!test.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
351 throw new BadRequestException("network UUID must match that of subnet");
353 if (ip.getIpAddress() != null) {
354 if (!subnet.isValidIP(ip.getIpAddress())) {
355 throw new BadRequestException("ip address not valid");
357 //TODO: need to add consideration for a fixed IP being assigned the same address as a allocated IP in the
359 if (subnet.isIPInUse(ip.getIpAddress())) {
360 throw new ResourceConflictException("IP address in use");
365 if (instances != null) {
366 if (instances.length > 0) {
367 for (Object instance : instances) {
368 INeutronPortAware service = (INeutronPortAware) instance;
369 int status = service.canCreatePort(test);
370 if (status < 200 || status > 299) {
371 return Response.status(status).build();
375 throw new ServiceUnavailableException("No providers registered. Please try again later");
378 throw new ServiceUnavailableException("Couldn't get providers list. Please try again later");
382 //once everything has passed, then we can add to the cache
384 while (i.hasNext()) {
385 NeutronPort test = i.next();
386 portInterface.addPort(test);
387 if (instances != null) {
388 for (Object instance : instances) {
389 INeutronPortAware service = (INeutronPortAware) instance;
390 service.neutronPortCreated(test);
395 return Response.status(201).entity(input).build();
403 @Produces({ MediaType.APPLICATION_JSON })
404 @Consumes({ MediaType.APPLICATION_JSON })
405 //@TypeHint(OpenStackPorts.class)
407 @ResponseCode(code = 200, condition = "Operation successful"),
408 @ResponseCode(code = 400, condition = "Bad Request"),
409 @ResponseCode(code = 401, condition = "Unauthorized"),
410 @ResponseCode(code = 403, condition = "Forbidden"),
411 @ResponseCode(code = 404, condition = "Not Found"),
412 @ResponseCode(code = 409, condition = "Conflict"),
413 @ResponseCode(code = 501, condition = "Not Implemented"),
414 @ResponseCode(code = 503, condition = "No providers available") })
415 public Response updatePort(
416 @PathParam("portUUID") String portUUID,
417 NeutronPortRequest input
419 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
420 if (portInterface == null) {
421 throw new ServiceUnavailableException("Port CRUD Interface "
422 + RestMessages.SERVICEUNAVAILABLE.toString());
424 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
425 if (subnetInterface == null) {
426 throw new ServiceUnavailableException("Subnet CRUD Interface "
427 + RestMessages.SERVICEUNAVAILABLE.toString());
430 // port has to exist and only a single delta is supported
431 if (!portInterface.portExists(portUUID)) {
432 throw new ResourceNotFoundException("port UUID does not exist.");
434 NeutronPort target = portInterface.getPort(portUUID);
435 if (!input.isSingleton()) {
436 throw new BadRequestException("only singleton edit suported");
438 NeutronPort singleton = input.getSingleton();
439 NeutronPort original = portInterface.getPort(portUUID);
441 // deltas restricted by Neutron
442 if (singleton.getID() != null || singleton.getTenantID() != null ||
443 singleton.getStatus() != null) {
444 throw new BadRequestException("attribute change blocked by Neutron");
447 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
448 if (instances != null) {
449 if (instances.length > 0) {
450 for (Object instance : instances) {
451 INeutronPortAware service = (INeutronPortAware) instance;
452 int status = service.canUpdatePort(singleton, original);
453 if (status < 200 || status > 299) {
454 return Response.status(status).build();
458 throw new ServiceUnavailableException("No providers registered. Please try again later");
461 throw new ServiceUnavailableException("Couldn't get providers list. Please try again later");
464 // Verify the new fixed ips are valid
465 List<Neutron_IPs> fixedIPs = singleton.getFixedIPs();
466 if (fixedIPs != null && fixedIPs.size() > 0) {
467 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
468 while (fixedIPIterator.hasNext()) {
469 Neutron_IPs ip = fixedIPIterator.next();
470 if (ip.getSubnetUUID() == null) {
471 throw new BadRequestException("subnet UUID must be specified");
473 if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
474 throw new BadRequestException("subnet UUID doesn't exist.");
476 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
477 if (!target.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
478 throw new BadRequestException("network UUID must match that of subnet");
480 if (ip.getIpAddress() != null) {
481 if (!subnet.isValidIP(ip.getIpAddress())) {
482 throw new BadRequestException("invalid IP address");
484 if (subnet.isIPInUse(ip.getIpAddress())) {
485 throw new ResourceConflictException("IP address in use");
491 // TODO: Support change of security groups
492 // update the port and return the modified object
493 portInterface.updatePort(portUUID, singleton);
494 NeutronPort updatedPort = portInterface.getPort(portUUID);
495 if (instances != null) {
496 for (Object instance : instances) {
497 INeutronPortAware service = (INeutronPortAware) instance;
498 service.neutronPortUpdated(updatedPort);
501 return Response.status(200).entity(
502 new NeutronPortRequest(updatedPort)).build();
512 @ResponseCode(code = 204, condition = "No Content"),
513 @ResponseCode(code = 401, condition = "Unauthorized"),
514 @ResponseCode(code = 403, condition = "Forbidden"),
515 @ResponseCode(code = 404, condition = "Not Found"),
516 @ResponseCode(code = 501, condition = "Not Implemented"),
517 @ResponseCode(code = 503, condition = "No providers available") })
518 public Response deletePort(
519 @PathParam("portUUID") String portUUID) {
520 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
521 if (portInterface == null) {
522 throw new ServiceUnavailableException("Port CRUD Interface "
523 + RestMessages.SERVICEUNAVAILABLE.toString());
526 // port has to exist and not be owned by anyone. then it can be removed from the cache
527 if (!portInterface.portExists(portUUID)) {
528 throw new ResourceNotFoundException("port UUID does not exist.");
530 NeutronPort port = portInterface.getPort(portUUID);
531 if (port.getDeviceID() != null ||
532 port.getDeviceOwner() != null) {
533 Response.status(403).build();
535 NeutronPort singleton = portInterface.getPort(portUUID);
536 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
537 if (instances != null) {
538 if (instances.length > 0) {
539 for (Object instance : instances) {
540 INeutronPortAware service = (INeutronPortAware) instance;
541 int status = service.canDeletePort(singleton);
542 if (status < 200 || status > 299) {
543 return Response.status(status).build();
547 throw new ServiceUnavailableException("No providers registered. Please try again later");
550 throw new ServiceUnavailableException("Couldn't get providers list. Please try again later");
552 portInterface.removePort(portUUID);
553 if (instances != null) {
554 for (Object instance : instances) {
555 INeutronPortAware service = (INeutronPortAware) instance;
556 service.neutronPortDeleted(singleton);
559 return Response.status(204).build();