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.net.HttpURLConnection;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
18 import javax.ws.rs.Consumes;
19 import javax.ws.rs.DELETE;
20 import javax.ws.rs.DefaultValue;
21 import javax.ws.rs.GET;
22 import javax.ws.rs.POST;
23 import javax.ws.rs.PUT;
24 import javax.ws.rs.Path;
25 import javax.ws.rs.PathParam;
26 import javax.ws.rs.Produces;
27 import javax.ws.rs.QueryParam;
28 import javax.ws.rs.core.Context;
29 import javax.ws.rs.core.MediaType;
30 import javax.ws.rs.core.Response;
31 import javax.ws.rs.core.UriInfo;
33 import org.codehaus.enunciate.jaxrs.ResponseCode;
34 import org.codehaus.enunciate.jaxrs.StatusCodes;
35 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
36 import org.opendaylight.neutron.spi.INeutronPortAware;
37 import org.opendaylight.neutron.spi.INeutronPortCRUD;
38 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
39 import org.opendaylight.neutron.spi.NeutronCRUDInterfaces;
40 import org.opendaylight.neutron.spi.NeutronPort;
41 import org.opendaylight.neutron.spi.NeutronSubnet;
42 import org.opendaylight.neutron.spi.Neutron_IPs;
45 * Neutron Northbound REST APIs.<br>
46 * This class provides REST APIs for managing neutron port objects
50 * Authentication scheme : <b>HTTP Basic</b><br>
51 * Authentication realm : <b>opendaylight</b><br>
52 * Transport : <b>HTTP and HTTPS</b><br>
54 * HTTPS Authentication is disabled by default. Administrator can enable it in
55 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
56 * trusted authority.<br>
58 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
63 public class NeutronPortsNorthbound {
65 private static final int HTTP_OK_BOTTOM = 200;
66 private static final int HTTP_OK_TOP = 299;
67 private static final String INTERFACE_NAME = "Port CRUD Interface";
68 private static final String UUID_NO_EXIST = "Port UUID does not exist.";
69 private static final String UUID_EXISTS = "Port UUID already exists.";
70 private static final String NO_PROVIDERS = "No providers registered. Please try again later";
71 private static final String NO_PROVIDER_LIST = "Couldn't get providers list. Please try again later";
72 private static final String NET_UUID_MATCH = "Network UUID must match that of subnet";
74 private static final String MAC_REGEX="^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$";
76 private NeutronPort extractFields(NeutronPort o, List<String> fields) {
77 return o.extractFields(fields);
80 private NeutronCRUDInterfaces getNeutronInterfaces(boolean needNetworks, boolean needSubnets) {
81 NeutronCRUDInterfaces answer = new NeutronCRUDInterfaces().fetchINeutronPortCRUD(this);
82 if (answer.getPortInterface() == null) {
83 throw new ServiceUnavailableException(INTERFACE_NAME
84 + RestMessages.SERVICEUNAVAILABLE.toString());
87 answer = answer.fetchINeutronNetworkCRUD( this);
88 if (answer.getNetworkInterface() == null) {
89 throw new ServiceUnavailableException("Network CRUD Interface "
90 + RestMessages.SERVICEUNAVAILABLE.toString());
94 answer = answer.fetchINeutronSubnetCRUD( this);
95 if (answer.getSubnetInterface() == null) {
96 throw new ServiceUnavailableException("Subnet CRUD Interface "
97 + RestMessages.SERVICEUNAVAILABLE.toString());
107 * Returns a list of all Ports */
110 @Produces({ MediaType.APPLICATION_JSON })
111 //@TypeHint(OpenStackPorts.class)
113 @ResponseCode(code = HttpURLConnection.HTTP_OK, condition = "Operation successful"),
114 @ResponseCode(code = HttpURLConnection.HTTP_UNAUTHORIZED, condition = "Unauthorized"),
115 @ResponseCode(code = HttpURLConnection.HTTP_NOT_IMPLEMENTED, condition = "Not Implemented"),
116 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "No providers available") })
117 public Response listPorts(
119 @QueryParam("fields") List<String> fields,
120 // note: openstack isn't clear about filtering on lists, so we aren't handling them
121 @QueryParam("id") String queryID,
122 @QueryParam("network_id") String queryNetworkID,
123 @QueryParam("name") String queryName,
124 @QueryParam("admin_state_up") String queryAdminStateUp,
125 @QueryParam("status") String queryStatus,
126 @QueryParam("mac_address") String queryMACAddress,
127 @QueryParam("device_id") String queryDeviceID,
128 @QueryParam("device_owner") String queryDeviceOwner,
129 @QueryParam("tenant_id") String queryTenantID,
131 @QueryParam("limit") Integer limit,
132 @QueryParam("marker") String marker,
133 @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
134 // sorting not supported
136 INeutronPortCRUD portInterface = getNeutronInterfaces(false, false).getPortInterface();
137 List<NeutronPort> allPorts = portInterface.getAllPorts();
138 List<NeutronPort> ans = new ArrayList<NeutronPort>();
139 Iterator<NeutronPort> i = allPorts.iterator();
140 while (i.hasNext()) {
141 NeutronPort oSS = i.next();
142 if ((queryID == null || queryID.equals(oSS.getID())) &&
143 (queryNetworkID == null || queryNetworkID.equals(oSS.getNetworkUUID())) &&
144 (queryName == null || queryName.equals(oSS.getName())) &&
145 (queryAdminStateUp == null || queryAdminStateUp.equals(oSS.getAdminStateUp())) &&
146 (queryStatus == null || queryStatus.equals(oSS.getStatus())) &&
147 (queryMACAddress == null || queryMACAddress.equals(oSS.getMacAddress())) &&
148 (queryDeviceID == null || queryDeviceID.equals(oSS.getDeviceID())) &&
149 (queryDeviceOwner == null || queryDeviceOwner.equals(oSS.getDeviceOwner())) &&
150 (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
151 if (fields.size() > 0) {
152 ans.add(extractFields(oSS,fields));
159 if (limit != null && ans.size() > 1) {
160 // Return a paginated request
161 NeutronPortRequest request = (NeutronPortRequest) PaginatedRequestFactory.createRequest(limit,
162 marker, pageReverse, uriInfo, ans, NeutronPort.class);
163 return Response.status(HttpURLConnection.HTTP_OK).entity(request).build();
166 return Response.status(HttpURLConnection.HTTP_OK).entity(
167 new NeutronPortRequest(ans)).build();
171 * Returns a specific Port */
175 @Produces({ MediaType.APPLICATION_JSON })
176 //@TypeHint(OpenStackPorts.class)
178 @ResponseCode(code = HttpURLConnection.HTTP_OK, condition = "Operation successful"),
179 @ResponseCode(code = HttpURLConnection.HTTP_UNAUTHORIZED, condition = "Unauthorized"),
180 @ResponseCode(code = HttpURLConnection.HTTP_NOT_FOUND, condition = "Not Found"),
181 @ResponseCode(code = HttpURLConnection.HTTP_NOT_IMPLEMENTED, condition = "Not Implemented"),
182 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "No providers available") })
183 public Response showPort(
184 @PathParam("portUUID") String portUUID,
186 @QueryParam("fields") List<String> fields ) {
187 INeutronPortCRUD portInterface = getNeutronInterfaces(false, false).getPortInterface();
188 if (!portInterface.portExists(portUUID)) {
189 throw new ResourceNotFoundException(UUID_NO_EXIST);
191 if (fields.size() > 0) {
192 NeutronPort ans = portInterface.getPort(portUUID);
193 return Response.status(HttpURLConnection.HTTP_OK).entity(
194 new NeutronPortRequest(extractFields(ans, fields))).build();
196 return Response.status(HttpURLConnection.HTTP_OK).entity(
197 new NeutronPortRequest(portInterface.getPort(portUUID))).build();
202 * Creates new Ports */
205 @Produces({ MediaType.APPLICATION_JSON })
206 @Consumes({ MediaType.APPLICATION_JSON })
207 //@TypeHint(OpenStackPorts.class)
209 @ResponseCode(code = HttpURLConnection.HTTP_CREATED, condition = "Created"),
210 @ResponseCode(code = HttpURLConnection.HTTP_BAD_REQUEST, condition = "Bad Request"),
211 @ResponseCode(code = HttpURLConnection.HTTP_UNAUTHORIZED, condition = "Unauthorized"),
212 @ResponseCode(code = HttpURLConnection.HTTP_FORBIDDEN, condition = "Forbidden"),
213 @ResponseCode(code = HttpURLConnection.HTTP_NOT_FOUND, condition = "Not Found"),
214 @ResponseCode(code = HttpURLConnection.HTTP_CONFLICT, condition = "Conflict"),
215 @ResponseCode(code = HttpURLConnection.HTTP_NOT_IMPLEMENTED, condition = "Not Implemented"),
216 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "MAC generation failure"),
217 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "No providers available") })
218 public Response createPorts(final NeutronPortRequest input) {
219 NeutronCRUDInterfaces interfaces = getNeutronInterfaces(true, true);
220 INeutronPortCRUD portInterface = interfaces.getPortInterface();
221 INeutronNetworkCRUD networkInterface = interfaces.getNetworkInterface();
222 INeutronSubnetCRUD subnetInterface = interfaces.getSubnetInterface();
223 if (input.isSingleton()) {
224 NeutronPort singleton = input.getSingleton();
227 * the port must be part of an existing network, must not already exist,
228 * have a valid MAC and the MAC not be in use
230 if (singleton.getNetworkUUID() == null) {
231 throw new BadRequestException("network UUID musy be specified");
233 if (portInterface.portExists(singleton.getID())) {
234 throw new BadRequestException(UUID_EXISTS);
236 if (!networkInterface.networkExists(singleton.getNetworkUUID())) {
237 throw new ResourceNotFoundException("network UUID does not exist.");
239 if (singleton.getMacAddress() == null ||
240 !singleton.getMacAddress().matches(MAC_REGEX)) {
241 throw new BadRequestException("MAC address not properly formatted");
243 if (portInterface.macInUse(singleton.getMacAddress())) {
244 throw new ResourceConflictException("MAC Address is in use.");
247 * if fixed IPs are specified, each one has to have an existing subnet ID
248 * that is in the same scoping network as the port. In addition, if an IP
249 * address is specified it has to be a valid address for the subnet and not
252 List<Neutron_IPs> fixedIPs = singleton.getFixedIPs();
253 if (fixedIPs != null && fixedIPs.size() > 0) {
254 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
255 while (fixedIPIterator.hasNext()) {
256 Neutron_IPs ip = fixedIPIterator.next();
257 if (ip.getSubnetUUID() == null) {
258 throw new BadRequestException("subnet UUID not specified");
260 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
261 if (subnet == null) {
262 throw new BadRequestException("subnet UUID must exist");
264 if (!singleton.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
265 throw new BadRequestException(NET_UUID_MATCH);
267 if (ip.getIpAddress() != null) {
268 if (!subnet.isValidIP(ip.getIpAddress())) {
269 throw new BadRequestException("IP address is not valid");
271 if (subnet.isIPInUse(ip.getIpAddress())) {
272 throw new ResourceConflictException("IP address is in use.");
278 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
279 if (instances != null) {
280 if (instances.length > 0) {
281 for (Object instance : instances) {
282 INeutronPortAware service = (INeutronPortAware) instance;
283 int status = service.canCreatePort(singleton);
284 if (status < HTTP_OK_BOTTOM || status > HTTP_OK_TOP) {
285 return Response.status(status).build();
289 throw new ServiceUnavailableException(NO_PROVIDERS);
292 throw new ServiceUnavailableException(NO_PROVIDER_LIST);
295 // add the port to the cache
296 portInterface.addPort(singleton);
297 if (instances != null) {
298 for (Object instance : instances) {
299 INeutronPortAware service = (INeutronPortAware) instance;
300 service.neutronPortCreated(singleton);
304 List<NeutronPort> bulk = input.getBulk();
305 Iterator<NeutronPort> i = bulk.iterator();
306 HashMap<String, NeutronPort> testMap = new HashMap<String, NeutronPort>();
307 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
308 while (i.hasNext()) {
309 NeutronPort test = i.next();
312 * the port must be part of an existing network, must not already exist,
313 * have a valid MAC and the MAC not be in use. Further the bulk request
314 * can't already contain a new port with the same UUID
316 if (portInterface.portExists(test.getID())) {
317 throw new BadRequestException(UUID_EXISTS);
319 if (testMap.containsKey(test.getID())) {
320 throw new BadRequestException(UUID_EXISTS);
322 for (NeutronPort check : testMap.values()) {
323 if (test.getMacAddress().equalsIgnoreCase(check.getMacAddress())) {
324 throw new ResourceConflictException("MAC address already allocated");
326 for (Neutron_IPs test_fixedIP : test.getFixedIPs()) {
327 for (Neutron_IPs check_fixedIP : check.getFixedIPs()) {
328 if (test_fixedIP.getSubnetUUID().equals(check_fixedIP.getSubnetUUID()) &&
329 (test_fixedIP.getIpAddress().equals(check_fixedIP.getIpAddress()))) {
330 throw new ResourceConflictException("IP address already allocated");
335 testMap.put(test.getID(), test);
336 if (!networkInterface.networkExists(test.getNetworkUUID())) {
337 throw new ResourceNotFoundException("network UUID does not exist.");
339 if (!test.getMacAddress().matches(MAC_REGEX)) {
340 throw new BadRequestException("MAC address not properly formatted");
342 if (portInterface.macInUse(test.getMacAddress())) {
343 throw new ResourceConflictException("MAC address in use");
347 * if fixed IPs are specified, each one has to have an existing subnet ID
348 * that is in the same scoping network as the port. In addition, if an IP
349 * address is specified it has to be a valid address for the subnet and not
350 * already in use (or be the gateway IP address of the subnet)
352 List<Neutron_IPs> fixedIPs = test.getFixedIPs();
353 if (fixedIPs != null && fixedIPs.size() > 0) {
354 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
355 while (fixedIPIterator.hasNext()) {
356 Neutron_IPs ip = fixedIPIterator.next();
357 if (ip.getSubnetUUID() == null) {
358 throw new BadRequestException("subnet UUID must be specified");
360 if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
361 throw new BadRequestException("subnet UUID doesn't exists");
363 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
364 if (!test.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
365 throw new BadRequestException(NET_UUID_MATCH);
367 if (ip.getIpAddress() != null) {
368 if (!subnet.isValidIP(ip.getIpAddress())) {
369 throw new BadRequestException("ip address not valid");
371 //TODO: need to add consideration for a fixed IP being assigned the same address as a allocated IP in the
373 if (subnet.isIPInUse(ip.getIpAddress())) {
374 throw new ResourceConflictException("IP address in use");
379 if (instances != null) {
380 if (instances.length > 0) {
381 for (Object instance : instances) {
382 INeutronPortAware service = (INeutronPortAware) instance;
383 int status = service.canCreatePort(test);
384 if (status < HTTP_OK_BOTTOM || status > HTTP_OK_TOP) {
385 return Response.status(status).build();
389 throw new ServiceUnavailableException(NO_PROVIDERS);
392 throw new ServiceUnavailableException(NO_PROVIDER_LIST);
396 //once everything has passed, then we can add to the cache
398 while (i.hasNext()) {
399 NeutronPort test = i.next();
400 portInterface.addPort(test);
401 if (instances != null) {
402 for (Object instance : instances) {
403 INeutronPortAware service = (INeutronPortAware) instance;
404 service.neutronPortCreated(test);
409 return Response.status(HttpURLConnection.HTTP_CREATED).entity(input).build();
417 @Produces({ MediaType.APPLICATION_JSON })
418 @Consumes({ MediaType.APPLICATION_JSON })
419 //@TypeHint(OpenStackPorts.class)
421 @ResponseCode(code = HttpURLConnection.HTTP_OK, condition = "Operation successful"),
422 @ResponseCode(code = HttpURLConnection.HTTP_BAD_REQUEST, condition = "Bad Request"),
423 @ResponseCode(code = HttpURLConnection.HTTP_UNAUTHORIZED, condition = "Unauthorized"),
424 @ResponseCode(code = HttpURLConnection.HTTP_FORBIDDEN, condition = "Forbidden"),
425 @ResponseCode(code = HttpURLConnection.HTTP_NOT_FOUND, condition = "Not Found"),
426 @ResponseCode(code = HttpURLConnection.HTTP_CONFLICT, condition = "Conflict"),
427 @ResponseCode(code = HttpURLConnection.HTTP_NOT_IMPLEMENTED, condition = "Not Implemented"),
428 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "No providers available") })
429 public Response updatePort(
430 @PathParam("portUUID") String portUUID,
431 NeutronPortRequest input
433 NeutronCRUDInterfaces interfaces = getNeutronInterfaces(false, true);
434 INeutronPortCRUD portInterface = interfaces.getPortInterface();
435 INeutronSubnetCRUD subnetInterface = interfaces.getSubnetInterface();
437 // port has to exist and only a single delta is supported
438 if (!portInterface.portExists(portUUID)) {
439 throw new ResourceNotFoundException(UUID_NO_EXIST);
441 NeutronPort target = portInterface.getPort(portUUID);
442 if (!input.isSingleton()) {
443 throw new BadRequestException("only singleton edit suported");
445 NeutronPort singleton = input.getSingleton();
446 NeutronPort original = portInterface.getPort(portUUID);
448 // deltas restricted by Neutron
449 if (singleton.getID() != null || singleton.getTenantID() != null ||
450 singleton.getStatus() != null) {
451 throw new BadRequestException("attribute change blocked by Neutron");
454 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
455 if (instances != null) {
456 if (instances.length > 0) {
457 for (Object instance : instances) {
458 INeutronPortAware service = (INeutronPortAware) instance;
459 int status = service.canUpdatePort(singleton, original);
460 if (status < HTTP_OK_BOTTOM || status > HTTP_OK_TOP) {
461 return Response.status(status).build();
465 throw new ServiceUnavailableException(NO_PROVIDERS);
468 throw new ServiceUnavailableException(NO_PROVIDER_LIST);
471 // Verify the new fixed ips are valid
472 List<Neutron_IPs> fixedIPs = singleton.getFixedIPs();
473 if (fixedIPs != null && fixedIPs.size() > 0) {
474 Iterator<Neutron_IPs> fixedIPIterator = fixedIPs.iterator();
475 while (fixedIPIterator.hasNext()) {
476 Neutron_IPs ip = fixedIPIterator.next();
477 if (ip.getSubnetUUID() == null) {
478 throw new BadRequestException("subnet UUID must be specified");
480 if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
481 throw new BadRequestException("subnet UUID doesn't exist.");
483 NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
484 if (!target.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
485 throw new BadRequestException(NET_UUID_MATCH);
487 if (ip.getIpAddress() != null) {
488 if (!subnet.isValidIP(ip.getIpAddress())) {
489 throw new BadRequestException("invalid IP address");
491 if (subnet.isIPInUse(ip.getIpAddress())) {
492 throw new ResourceConflictException("IP address in use");
498 // TODO: Support change of security groups
499 // update the port and return the modified object
500 portInterface.updatePort(portUUID, singleton);
501 NeutronPort updatedPort = portInterface.getPort(portUUID);
502 if (instances != null) {
503 for (Object instance : instances) {
504 INeutronPortAware service = (INeutronPortAware) instance;
505 service.neutronPortUpdated(updatedPort);
508 return Response.status(HttpURLConnection.HTTP_OK).entity(
509 new NeutronPortRequest(updatedPort)).build();
519 @ResponseCode(code = HttpURLConnection.HTTP_NO_CONTENT, condition = "No Content"),
520 @ResponseCode(code = HttpURLConnection.HTTP_UNAUTHORIZED, condition = "Unauthorized"),
521 @ResponseCode(code = HttpURLConnection.HTTP_FORBIDDEN, condition = "Forbidden"),
522 @ResponseCode(code = HttpURLConnection.HTTP_NOT_FOUND, condition = "Not Found"),
523 @ResponseCode(code = HttpURLConnection.HTTP_NOT_IMPLEMENTED, condition = "Not Implemented"),
524 @ResponseCode(code = HttpURLConnection.HTTP_UNAVAILABLE, condition = "No providers available") })
525 public Response deletePort(
526 @PathParam("portUUID") String portUUID) {
527 INeutronPortCRUD portInterface = getNeutronInterfaces(false, false).getPortInterface();
529 // port has to exist and not be owned by anyone. then it can be removed from the cache
530 if (!portInterface.portExists(portUUID)) {
531 throw new ResourceNotFoundException(UUID_NO_EXIST);
533 NeutronPort port = portInterface.getPort(portUUID);
534 if (port.getDeviceID() != null ||
535 port.getDeviceOwner() != null) {
536 Response.status(HttpURLConnection.HTTP_FORBIDDEN).build();
538 NeutronPort singleton = portInterface.getPort(portUUID);
539 Object[] instances = NeutronUtil.getInstances(INeutronPortAware.class, this);
540 if (instances != null) {
541 if (instances.length > 0) {
542 for (Object instance : instances) {
543 INeutronPortAware service = (INeutronPortAware) instance;
544 int status = service.canDeletePort(singleton);
545 if (status < HTTP_OK_BOTTOM || status > HTTP_OK_TOP) {
546 return Response.status(status).build();
550 throw new ServiceUnavailableException(NO_PROVIDERS);
553 throw new ServiceUnavailableException(NO_PROVIDER_LIST);
555 portInterface.removePort(portUUID);
556 if (instances != null) {
557 for (Object instance : instances) {
558 INeutronPortAware service = (INeutronPortAware) instance;
559 service.neutronPortDeleted(singleton);
562 return Response.status(HttpURLConnection.HTTP_NO_CONTENT).build();