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.INeutronNetworkCRUD;
29 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
30 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterAware;
31 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterCRUD;
32 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
33 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
34 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
35 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
36 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter;
37 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
38 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
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;
48 * Neutron Northbound REST APIs.<br>
49 * This class provides REST APIs for managing neutron routers
53 * Authentication scheme : <b>HTTP Basic</b><br>
54 * Authentication realm : <b>opendaylight</b><br>
55 * Transport : <b>HTTP and HTTPS</b><br>
57 * HTTPS Authentication is disabled by default. Administrator can enable it in
58 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
59 * trusted authority.<br>
61 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
66 public class NeutronRoutersNorthbound {
68 private NeutronRouter extractFields(NeutronRouter o, List<String> fields) {
69 return o.extractFields(fields);
73 * Returns a list of all Routers */
76 @Produces({ MediaType.APPLICATION_JSON })
77 //@TypeHint(OpenStackRouters.class)
79 @ResponseCode(code = 200, condition = "Operation successful"),
80 @ResponseCode(code = 401, condition = "Unauthorized"),
81 @ResponseCode(code = 501, condition = "Not Implemented") })
82 public Response listRouters(
84 @QueryParam("fields") List<String> fields,
85 // note: openstack isn't clear about filtering on lists, so we aren't handling them
86 @QueryParam("id") String queryID,
87 @QueryParam("name") String queryName,
88 @QueryParam("admin_state_up") String queryAdminStateUp,
89 @QueryParam("status") String queryStatus,
90 @QueryParam("tenant_id") String queryTenantID,
91 @QueryParam("external_gateway_info") String queryExternalGatewayInfo,
93 @QueryParam("limit") String limit,
94 @QueryParam("marker") String marker,
95 @QueryParam("page_reverse") String pageReverse
96 // sorting not supported
98 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
99 if (routerInterface == null) {
100 throw new ServiceUnavailableException("Router CRUD Interface "
101 + RestMessages.SERVICEUNAVAILABLE.toString());
103 List<NeutronRouter> allRouters = routerInterface.getAllRouters();
104 List<NeutronRouter> ans = new ArrayList<NeutronRouter>();
105 Iterator<NeutronRouter> i = allRouters.iterator();
106 while (i.hasNext()) {
107 NeutronRouter oSS = i.next();
108 if ((queryID == null || queryID.equals(oSS.getID())) &&
109 (queryName == null || queryName.equals(oSS.getName())) &&
110 (queryAdminStateUp == null || queryAdminStateUp.equals(oSS.getAdminStateUp())) &&
111 (queryStatus == null || queryStatus.equals(oSS.getStatus())) &&
112 (queryExternalGatewayInfo == null || queryExternalGatewayInfo.equals(oSS.getExternalGatewayInfo())) &&
113 (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
114 if (fields.size() > 0)
115 ans.add(extractFields(oSS,fields));
120 //TODO: apply pagination to results
121 return Response.status(200).entity(
122 new NeutronRouterRequest(ans)).build();
126 * Returns a specific Router */
128 @Path("{routerUUID}")
130 @Produces({ MediaType.APPLICATION_JSON })
131 //@TypeHint(OpenStackRouters.class)
133 @ResponseCode(code = 200, condition = "Operation successful"),
134 @ResponseCode(code = 401, condition = "Unauthorized"),
135 @ResponseCode(code = 403, condition = "Forbidden"),
136 @ResponseCode(code = 404, condition = "Not Found"),
137 @ResponseCode(code = 501, condition = "Not Implemented") })
138 public Response showRouter(
139 @PathParam("routerUUID") String routerUUID,
141 @QueryParam("fields") List<String> fields) {
142 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
143 if (routerInterface == null) {
144 throw new ServiceUnavailableException("Router CRUD Interface "
145 + RestMessages.SERVICEUNAVAILABLE.toString());
147 if (!routerInterface.routerExists(routerUUID)) {
148 throw new ResourceNotFoundException("Router UUID not found");
150 if (fields.size() > 0) {
151 NeutronRouter ans = routerInterface.getRouter(routerUUID);
152 return Response.status(200).entity(
153 new NeutronRouterRequest(extractFields(ans, fields))).build();
155 return Response.status(200).entity(
156 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
160 * Creates new Routers */
163 @Produces({ MediaType.APPLICATION_JSON })
164 @Consumes({ MediaType.APPLICATION_JSON })
165 //@TypeHint(OpenStackRouters.class)
167 @ResponseCode(code = 201, condition = "Created"),
168 @ResponseCode(code = 400, condition = "Bad Request"),
169 @ResponseCode(code = 401, condition = "Unauthorized"),
170 @ResponseCode(code = 501, condition = "Not Implemented") })
171 public Response createRouters(final NeutronRouterRequest input) {
172 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
173 if (routerInterface == null) {
174 throw new ServiceUnavailableException("Router CRUD Interface "
175 + RestMessages.SERVICEUNAVAILABLE.toString());
177 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
178 if (networkInterface == null) {
179 throw new ServiceUnavailableException("Network CRUD Interface "
180 + RestMessages.SERVICEUNAVAILABLE.toString());
182 if (input.isSingleton()) {
183 NeutronRouter singleton = input.getSingleton();
186 * verify that the router doesn't already exist (issue: is deeper inspection necessary?)
187 * if there is external gateway information provided, verify that the specified network
188 * exists and has been designated as "router:external"
190 if (routerInterface.routerExists(singleton.getID()))
191 throw new BadRequestException("router UUID already exists");
192 if (singleton.getExternalGatewayInfo() != null) {
193 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
194 if (!networkInterface.networkExists(externNetworkPtr))
195 throw new BadRequestException("External Network Pointer doesn't exist");
196 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
197 if (!externNetwork.isRouterExternal())
198 throw new BadRequestException("External Network Pointer isn't marked as router:external");
200 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
201 if (instances != null) {
202 for (Object instance : instances) {
203 INeutronRouterAware service = (INeutronRouterAware) instance;
204 int status = service.canCreateRouter(singleton);
205 if (status < 200 || status > 299)
206 return Response.status(status).build();
211 * add router to the cache
213 routerInterface.addRouter(singleton);
214 if (instances != null) {
215 for (Object instance : instances) {
216 INeutronRouterAware service = (INeutronRouterAware) instance;
217 service.neutronRouterCreated(singleton);
223 * only singleton router creates supported
225 throw new BadRequestException("Only singleton router creates supported");
227 return Response.status(201).entity(input).build();
231 * Updates a Router */
233 @Path("{routerUUID}")
235 @Produces({ MediaType.APPLICATION_JSON })
236 @Consumes({ MediaType.APPLICATION_JSON })
237 //@TypeHint(OpenStackRouters.class)
239 @ResponseCode(code = 200, condition = "Operation successful"),
240 @ResponseCode(code = 400, condition = "Bad Request"),
241 @ResponseCode(code = 401, condition = "Unauthorized"),
242 @ResponseCode(code = 404, condition = "Not Found"),
243 @ResponseCode(code = 501, condition = "Not Implemented") })
244 public Response updateRouter(
245 @PathParam("routerUUID") String routerUUID,
246 NeutronRouterRequest input
248 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
249 if (routerInterface == null) {
250 throw new ServiceUnavailableException("Router CRUD Interface "
251 + RestMessages.SERVICEUNAVAILABLE.toString());
253 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
254 if (networkInterface == null) {
255 throw new ServiceUnavailableException("Network CRUD Interface "
256 + RestMessages.SERVICEUNAVAILABLE.toString());
260 * router has to exist and only a single delta can be supplied
262 if (!routerInterface.routerExists(routerUUID))
263 throw new ResourceNotFoundException("Router UUID not found");
264 if (!input.isSingleton())
265 throw new BadRequestException("Only single router deltas supported");
266 NeutronRouter singleton = input.getSingleton();
267 NeutronRouter original = routerInterface.getRouter(routerUUID);
270 * attribute changes blocked by Neutron
272 if (singleton.getID() != null || singleton.getTenantID() != null ||
273 singleton.getStatus() != null)
274 throw new BadRequestException("Request attribute change not allowed");
276 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
277 if (instances != null) {
278 for (Object instance : instances) {
279 INeutronRouterAware service = (INeutronRouterAware) instance;
280 int status = service.canUpdateRouter(singleton, original);
281 if (status < 200 || status > 299)
282 return Response.status(status).build();
286 * if the external gateway info is being changed, verify that the new network
287 * exists and has been designated as an external network
289 if (singleton.getExternalGatewayInfo() != null) {
290 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
291 if (!networkInterface.networkExists(externNetworkPtr))
292 throw new BadRequestException("External Network Pointer does not exist");
293 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
294 if (!externNetwork.isRouterExternal())
295 throw new BadRequestException("External Network Pointer isn't marked as router:external");
299 * update the router entry and return the modified object
301 routerInterface.updateRouter(routerUUID, singleton);
302 NeutronRouter updatedRouter = routerInterface.getRouter(routerUUID);
303 if (instances != null) {
304 for (Object instance : instances) {
305 INeutronRouterAware service = (INeutronRouterAware) instance;
306 service.neutronRouterUpdated(updatedRouter);
309 return Response.status(200).entity(
310 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
315 * Deletes a Router */
317 @Path("{routerUUID}")
320 @ResponseCode(code = 204, condition = "No Content"),
321 @ResponseCode(code = 401, condition = "Unauthorized"),
322 @ResponseCode(code = 404, condition = "Not Found"),
323 @ResponseCode(code = 409, condition = "Conflict"),
324 @ResponseCode(code = 501, condition = "Not Implemented") })
325 public Response deleteRouter(
326 @PathParam("routerUUID") String routerUUID) {
327 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
328 if (routerInterface == null) {
329 throw new ServiceUnavailableException("Router CRUD Interface "
330 + RestMessages.SERVICEUNAVAILABLE.toString());
334 * verify that the router exists and is not in use before removing it
336 if (!routerInterface.routerExists(routerUUID))
337 throw new ResourceNotFoundException("Router UUID not found");
338 if (routerInterface.routerInUse(routerUUID))
339 throw new ResourceConflictException("Router UUID in Use");
340 NeutronRouter singleton = routerInterface.getRouter(routerUUID);
341 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
342 if (instances != null) {
343 for (Object instance : instances) {
344 INeutronRouterAware service = (INeutronRouterAware) instance;
345 int status = service.canDeleteRouter(singleton);
346 if (status < 200 || status > 299)
347 return Response.status(status).build();
350 routerInterface.removeRouter(routerUUID);
351 if (instances != null) {
352 for (Object instance : instances) {
353 INeutronRouterAware service = (INeutronRouterAware) instance;
354 service.neutronRouterDeleted(singleton);
357 return Response.status(204).build();
361 * Adds an interface to a router */
363 @Path("{routerUUID}/add_router_interface")
365 @Produces({ MediaType.APPLICATION_JSON })
366 @Consumes({ MediaType.APPLICATION_JSON })
367 //@TypeHint(OpenStackRouterInterfaces.class)
369 @ResponseCode(code = 200, condition = "Operation successful"),
370 @ResponseCode(code = 400, condition = "Bad Request"),
371 @ResponseCode(code = 401, condition = "Unauthorized"),
372 @ResponseCode(code = 404, condition = "Not Found"),
373 @ResponseCode(code = 409, condition = "Conflict"),
374 @ResponseCode(code = 501, condition = "Not Implemented") })
375 public Response addRouterInterface(
376 @PathParam("routerUUID") String routerUUID,
377 NeutronRouter_Interface input
379 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
380 if (routerInterface == null) {
381 throw new ServiceUnavailableException("Router CRUD Interface "
382 + RestMessages.SERVICEUNAVAILABLE.toString());
384 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
385 if (portInterface == null) {
386 throw new ServiceUnavailableException("Port CRUD Interface "
387 + RestMessages.SERVICEUNAVAILABLE.toString());
389 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
390 if (subnetInterface == null) {
391 throw new ServiceUnavailableException("Subnet CRUD Interface "
392 + RestMessages.SERVICEUNAVAILABLE.toString());
396 * While the Neutron specification says that the router has to exist and the input can only specify either a subnet id
397 * or a port id, but not both, this code assumes that the plugin has filled everything in for us and so both must be present
399 if (!routerInterface.routerExists(routerUUID))
400 throw new BadRequestException("Router UUID doesn't exist");
401 NeutronRouter target = routerInterface.getRouter(routerUUID);
402 if (input.getSubnetUUID() == null ||
403 input.getPortUUID() == null)
404 throw new BadRequestException("Must specify at subnet id, port id or both");
406 // check that the port is part of the subnet
407 NeutronSubnet targetSubnet = subnetInterface.getSubnet(input.getSubnetUUID());
408 if (targetSubnet == null)
409 throw new BadRequestException("Subnet id doesn't exist");
410 NeutronPort targetPort = portInterface.getPort(input.getPortUUID());
411 if (targetPort == null)
412 throw new BadRequestException("Port id doesn't exist");
413 if (!targetSubnet.getPortsInSubnet().contains(targetPort))
414 throw new BadRequestException("Port id not part of subnet id");
416 if (targetPort.getFixedIPs().size() != 1)
417 throw new BadRequestException("Port id must have a single fixedIP address");
418 if (targetPort.getDeviceID() != null ||
419 targetPort.getDeviceOwner() != null)
420 throw new ResourceConflictException("Target Port already allocated");
421 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
422 if (instances != null) {
423 for (Object instance : instances) {
424 INeutronRouterAware service = (INeutronRouterAware) instance;
425 int status = service.canAttachInterface(target, input);
426 if (status < 200 || status > 299)
427 return Response.status(status).build();
431 //mark the port device id and device owner fields
432 targetPort.setDeviceOwner("network:router_interface");
433 targetPort.setDeviceID(routerUUID);
435 target.addInterface(input.getPortUUID(), input);
436 if (instances != null) {
437 for (Object instance : instances) {
438 INeutronRouterAware service = (INeutronRouterAware) instance;
439 service.neutronRouterInterfaceAttached(target, input);
443 return Response.status(200).entity(input).build();
447 * Removes an interface to a router */
449 @Path("{routerUUID}/remove_router_interface")
451 @Produces({ MediaType.APPLICATION_JSON })
452 @Consumes({ MediaType.APPLICATION_JSON })
453 //@TypeHint(OpenStackRouterInterfaces.class)
455 @ResponseCode(code = 200, condition = "Operation successful"),
456 @ResponseCode(code = 400, condition = "Bad Request"),
457 @ResponseCode(code = 401, condition = "Unauthorized"),
458 @ResponseCode(code = 404, condition = "Not Found"),
459 @ResponseCode(code = 409, condition = "Conflict"),
460 @ResponseCode(code = 501, condition = "Not Implemented") })
461 public Response removeRouterInterface(
462 @PathParam("routerUUID") String routerUUID,
463 NeutronRouter_Interface input
465 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
466 if (routerInterface == null) {
467 throw new ServiceUnavailableException("Router CRUD Interface "
468 + RestMessages.SERVICEUNAVAILABLE.toString());
470 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
471 if (portInterface == null) {
472 throw new ServiceUnavailableException("Port CRUD Interface "
473 + RestMessages.SERVICEUNAVAILABLE.toString());
475 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
476 if (subnetInterface == null) {
477 throw new ServiceUnavailableException("Subnet CRUD Interface "
478 + RestMessages.SERVICEUNAVAILABLE.toString());
481 // verify the router exists
482 if (!routerInterface.routerExists(routerUUID))
483 throw new BadRequestException("Router does not exist");
484 NeutronRouter target = routerInterface.getRouter(routerUUID);
487 * remove by subnet id. Collect information about the impacted router for the response and
488 * remove the port corresponding to the gateway IP address of the subnet
490 if (input.getPortUUID() == null &&
491 input.getSubnetUUID() != null) {
492 NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
494 throw new ResourceNotFoundException("Port UUID not found");
495 input.setPortUUID(port.getID());
496 input.setID(target.getID());
497 input.setTenantID(target.getTenantID());
499 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
500 if (instances != null) {
501 for (Object instance : instances) {
502 INeutronRouterAware service = (INeutronRouterAware) instance;
503 int status = service.canDetachInterface(target, input);
504 if (status < 200 || status > 299)
505 return Response.status(status).build();
509 // reset the port ownership
510 port.setDeviceID(null);
511 port.setDeviceOwner(null);
513 target.removeInterface(input.getPortUUID());
514 if (instances != null) {
515 for (Object instance : instances) {
516 INeutronRouterAware service = (INeutronRouterAware) instance;
517 service.neutronRouterInterfaceDetached(target, input);
520 return Response.status(200).entity(input).build();
524 * remove by port id. collect information about the impacted router for the response
525 * remove the interface and reset the port ownership
527 if (input.getPortUUID() != null &&
528 input.getSubnetUUID() == null) {
529 NeutronRouter_Interface targetInterface = target.getInterfaces().get(input.getPortUUID());
530 if (targetInterface == null) {
531 throw new ResourceNotFoundException("Router interface not found for given Port UUID");
533 input.setSubnetUUID(targetInterface.getSubnetUUID());
534 input.setID(target.getID());
535 input.setTenantID(target.getTenantID());
536 NeutronPort port = portInterface.getPort(input.getPortUUID());
537 port.setDeviceID(null);
538 port.setDeviceOwner(null);
539 target.removeInterface(input.getPortUUID());
540 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
541 for (Object instance : instances) {
542 INeutronRouterAware service = (INeutronRouterAware) instance;
543 service.neutronRouterInterfaceDetached(target, input);
545 return Response.status(200).entity(input).build();
549 * remove by both port and subnet ID. Verify that the first fixed IP of the port is a valid
550 * IP address for the subnet, and then remove the interface, collecting information about the
551 * impacted router for the response and reset port ownership
553 if (input.getPortUUID() != null &&
554 input.getSubnetUUID() != null) {
555 NeutronPort port = portInterface.getPort(input.getPortUUID());
557 throw new ResourceNotFoundException("Port UUID not found");
559 if (port.getFixedIPs() == null) {
560 throw new ResourceNotFoundException("Port UUID has no fixed IPs");
562 NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
563 if (subnet == null) {
564 throw new ResourceNotFoundException("Subnet UUID not found");
566 if (!subnet.isValidIP(port.getFixedIPs().get(0).getIpAddress()))
567 throw new ResourceConflictException("Target Port IP not in Target Subnet");
568 input.setID(target.getID());
569 input.setTenantID(target.getTenantID());
570 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
571 if (instances != null) {
572 for (Object instance : instances) {
573 INeutronRouterAware service = (INeutronRouterAware) instance;
574 service.canDetachInterface(target, input);
577 port.setDeviceID(null);
578 port.setDeviceOwner(null);
579 target.removeInterface(input.getPortUUID());
580 for (Object instance : instances) {
581 INeutronRouterAware service = (INeutronRouterAware) instance;
582 service.neutronRouterInterfaceDetached(target, input);
584 return Response.status(200).entity(input).build();
587 // have to specify either a port ID or a subnet ID
588 throw new BadRequestException("Must specify port id or subnet id or both");