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;
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;
27 import org.codehaus.enunciate.jaxrs.ResponseCode;
28 import org.codehaus.enunciate.jaxrs.StatusCodes;
29 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
30 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
31 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterAware;
32 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterCRUD;
33 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
34 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
35 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
36 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
37 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter;
38 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
39 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
43 * Neutron Northbound REST APIs.<br>
44 * This class provides REST APIs for managing neutron routers
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 NeutronRoutersNorthbound {
63 private NeutronRouter extractFields(NeutronRouter o, List<String> fields) {
64 return o.extractFields(fields);
68 * Returns a list of all Routers */
71 @Produces({ MediaType.APPLICATION_JSON })
72 //@TypeHint(OpenStackRouters.class)
74 @ResponseCode(code = 200, condition = "Operation successful"),
75 @ResponseCode(code = 401, condition = "Unauthorized"),
76 @ResponseCode(code = 501, condition = "Not Implemented") })
77 public Response listRouters(
79 @QueryParam("fields") List<String> fields,
80 // note: openstack isn't clear about filtering on lists, so we aren't handling them
81 @QueryParam("id") String queryID,
82 @QueryParam("name") String queryName,
83 @QueryParam("admin_state_up") String queryAdminStateUp,
84 @QueryParam("status") String queryStatus,
85 @QueryParam("tenant_id") String queryTenantID,
86 @QueryParam("external_gateway_info") String queryExternalGatewayInfo,
88 @QueryParam("limit") String limit,
89 @QueryParam("marker") String marker,
90 @QueryParam("page_reverse") String pageReverse
91 // sorting not supported
93 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
94 if (routerInterface == null) {
95 throw new ServiceUnavailableException("Router CRUD Interface "
96 + RestMessages.SERVICEUNAVAILABLE.toString());
98 List<NeutronRouter> allRouters = routerInterface.getAllRouters();
99 List<NeutronRouter> ans = new ArrayList<NeutronRouter>();
100 Iterator<NeutronRouter> i = allRouters.iterator();
101 while (i.hasNext()) {
102 NeutronRouter oSS = i.next();
103 if ((queryID == null || queryID.equals(oSS.getID())) &&
104 (queryName == null || queryName.equals(oSS.getName())) &&
105 (queryAdminStateUp == null || queryAdminStateUp.equals(oSS.getAdminStateUp())) &&
106 (queryStatus == null || queryStatus.equals(oSS.getStatus())) &&
107 (queryExternalGatewayInfo == null || queryExternalGatewayInfo.equals(oSS.getExternalGatewayInfo())) &&
108 (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
109 if (fields.size() > 0)
110 ans.add(extractFields(oSS,fields));
115 //TODO: apply pagination to results
116 return Response.status(200).entity(
117 new NeutronRouterRequest(ans)).build();
121 * Returns a specific Router */
123 @Path("{routerUUID}")
125 @Produces({ MediaType.APPLICATION_JSON })
126 //@TypeHint(OpenStackRouters.class)
128 @ResponseCode(code = 200, condition = "Operation successful"),
129 @ResponseCode(code = 401, condition = "Unauthorized"),
130 @ResponseCode(code = 403, condition = "Forbidden"),
131 @ResponseCode(code = 404, condition = "Not Found"),
132 @ResponseCode(code = 501, condition = "Not Implemented") })
133 public Response showRouter(
134 @PathParam("routerUUID") String routerUUID,
136 @QueryParam("fields") List<String> fields) {
137 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
138 if (routerInterface == null) {
139 throw new ServiceUnavailableException("Router CRUD Interface "
140 + RestMessages.SERVICEUNAVAILABLE.toString());
142 if (!routerInterface.routerExists(routerUUID)) {
143 throw new ResourceNotFoundException("Router UUID not found");
145 if (fields.size() > 0) {
146 NeutronRouter ans = routerInterface.getRouter(routerUUID);
147 return Response.status(200).entity(
148 new NeutronRouterRequest(extractFields(ans, fields))).build();
150 return Response.status(200).entity(
151 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
155 * Creates new Routers */
158 @Produces({ MediaType.APPLICATION_JSON })
159 @Consumes({ MediaType.APPLICATION_JSON })
160 //@TypeHint(OpenStackRouters.class)
162 @ResponseCode(code = 201, condition = "Created"),
163 @ResponseCode(code = 400, condition = "Bad Request"),
164 @ResponseCode(code = 401, condition = "Unauthorized"),
165 @ResponseCode(code = 501, condition = "Not Implemented") })
166 public Response createRouters(final NeutronRouterRequest input) {
167 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
168 if (routerInterface == null) {
169 throw new ServiceUnavailableException("Router CRUD Interface "
170 + RestMessages.SERVICEUNAVAILABLE.toString());
172 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
173 if (networkInterface == null) {
174 throw new ServiceUnavailableException("Network CRUD Interface "
175 + RestMessages.SERVICEUNAVAILABLE.toString());
177 if (input.isSingleton()) {
178 NeutronRouter singleton = input.getSingleton();
181 * verify that the router doesn't already exist (issue: is deeper inspection necessary?)
182 * if there is external gateway information provided, verify that the specified network
183 * exists and has been designated as "router:external"
185 if (routerInterface.routerExists(singleton.getID()))
186 throw new BadRequestException("router UUID already exists");
187 if (singleton.getExternalGatewayInfo() != null) {
188 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
189 if (!networkInterface.networkExists(externNetworkPtr))
190 throw new BadRequestException("External Network Pointer doesn't exist");
191 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
192 if (!externNetwork.isRouterExternal())
193 throw new BadRequestException("External Network Pointer isn't marked as router:external");
195 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
196 if (instances != null) {
197 for (Object instance : instances) {
198 INeutronRouterAware service = (INeutronRouterAware) instance;
199 int status = service.canCreateRouter(singleton);
200 if (status < 200 || status > 299)
201 return Response.status(status).build();
206 * add router to the cache
208 routerInterface.addRouter(singleton);
209 if (instances != null) {
210 for (Object instance : instances) {
211 INeutronRouterAware service = (INeutronRouterAware) instance;
212 service.neutronRouterCreated(singleton);
218 * only singleton router creates supported
220 throw new BadRequestException("Only singleton router creates supported");
222 return Response.status(201).entity(input).build();
226 * Updates a Router */
228 @Path("{routerUUID}")
230 @Produces({ MediaType.APPLICATION_JSON })
231 @Consumes({ MediaType.APPLICATION_JSON })
232 //@TypeHint(OpenStackRouters.class)
234 @ResponseCode(code = 200, condition = "Operation successful"),
235 @ResponseCode(code = 400, condition = "Bad Request"),
236 @ResponseCode(code = 401, condition = "Unauthorized"),
237 @ResponseCode(code = 404, condition = "Not Found"),
238 @ResponseCode(code = 501, condition = "Not Implemented") })
239 public Response updateRouter(
240 @PathParam("routerUUID") String routerUUID,
241 NeutronRouterRequest input
243 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
244 if (routerInterface == null) {
245 throw new ServiceUnavailableException("Router CRUD Interface "
246 + RestMessages.SERVICEUNAVAILABLE.toString());
248 INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
249 if (networkInterface == null) {
250 throw new ServiceUnavailableException("Network CRUD Interface "
251 + RestMessages.SERVICEUNAVAILABLE.toString());
255 * router has to exist and only a single delta can be supplied
257 if (!routerInterface.routerExists(routerUUID))
258 throw new ResourceNotFoundException("Router UUID not found");
259 if (!input.isSingleton())
260 throw new BadRequestException("Only single router deltas supported");
261 NeutronRouter singleton = input.getSingleton();
262 NeutronRouter original = routerInterface.getRouter(routerUUID);
265 * attribute changes blocked by Neutron
267 if (singleton.getID() != null || singleton.getTenantID() != null ||
268 singleton.getStatus() != null)
269 throw new BadRequestException("Request attribute change not allowed");
271 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
272 if (instances != null) {
273 for (Object instance : instances) {
274 INeutronRouterAware service = (INeutronRouterAware) instance;
275 int status = service.canUpdateRouter(singleton, original);
276 if (status < 200 || status > 299)
277 return Response.status(status).build();
281 * if the external gateway info is being changed, verify that the new network
282 * exists and has been designated as an external network
284 if (singleton.getExternalGatewayInfo() != null) {
285 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
286 if (!networkInterface.networkExists(externNetworkPtr))
287 throw new BadRequestException("External Network Pointer does not exist");
288 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
289 if (!externNetwork.isRouterExternal())
290 throw new BadRequestException("External Network Pointer isn't marked as router:external");
294 * update the router entry and return the modified object
296 routerInterface.updateRouter(routerUUID, singleton);
297 NeutronRouter updatedRouter = routerInterface.getRouter(routerUUID);
298 if (instances != null) {
299 for (Object instance : instances) {
300 INeutronRouterAware service = (INeutronRouterAware) instance;
301 service.neutronRouterUpdated(updatedRouter);
304 return Response.status(200).entity(
305 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
310 * Deletes a Router */
312 @Path("{routerUUID}")
315 @ResponseCode(code = 204, condition = "No Content"),
316 @ResponseCode(code = 401, condition = "Unauthorized"),
317 @ResponseCode(code = 404, condition = "Not Found"),
318 @ResponseCode(code = 409, condition = "Conflict"),
319 @ResponseCode(code = 501, condition = "Not Implemented") })
320 public Response deleteRouter(
321 @PathParam("routerUUID") String routerUUID) {
322 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
323 if (routerInterface == null) {
324 throw new ServiceUnavailableException("Router CRUD Interface "
325 + RestMessages.SERVICEUNAVAILABLE.toString());
329 * verify that the router exists and is not in use before removing it
331 if (!routerInterface.routerExists(routerUUID))
332 throw new ResourceNotFoundException("Router UUID not found");
333 if (routerInterface.routerInUse(routerUUID))
334 throw new ResourceConflictException("Router UUID in Use");
335 NeutronRouter singleton = routerInterface.getRouter(routerUUID);
336 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
337 if (instances != null) {
338 for (Object instance : instances) {
339 INeutronRouterAware service = (INeutronRouterAware) instance;
340 int status = service.canDeleteRouter(singleton);
341 if (status < 200 || status > 299)
342 return Response.status(status).build();
345 routerInterface.removeRouter(routerUUID);
346 if (instances != null) {
347 for (Object instance : instances) {
348 INeutronRouterAware service = (INeutronRouterAware) instance;
349 service.neutronRouterDeleted(singleton);
352 return Response.status(204).build();
356 * Adds an interface to a router */
358 @Path("{routerUUID}/add_router_interface")
360 @Produces({ MediaType.APPLICATION_JSON })
361 @Consumes({ MediaType.APPLICATION_JSON })
362 //@TypeHint(OpenStackRouterInterfaces.class)
364 @ResponseCode(code = 200, condition = "Operation successful"),
365 @ResponseCode(code = 400, condition = "Bad Request"),
366 @ResponseCode(code = 401, condition = "Unauthorized"),
367 @ResponseCode(code = 404, condition = "Not Found"),
368 @ResponseCode(code = 409, condition = "Conflict"),
369 @ResponseCode(code = 501, condition = "Not Implemented") })
370 public Response addRouterInterface(
371 @PathParam("routerUUID") String routerUUID,
372 NeutronRouter_Interface input
374 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
375 if (routerInterface == null) {
376 throw new ServiceUnavailableException("Router CRUD Interface "
377 + RestMessages.SERVICEUNAVAILABLE.toString());
379 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
380 if (portInterface == null) {
381 throw new ServiceUnavailableException("Port CRUD Interface "
382 + RestMessages.SERVICEUNAVAILABLE.toString());
384 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
385 if (subnetInterface == null) {
386 throw new ServiceUnavailableException("Subnet CRUD Interface "
387 + RestMessages.SERVICEUNAVAILABLE.toString());
391 * While the Neutron specification says that the router has to exist and the input can only specify either a subnet id
392 * 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
394 if (!routerInterface.routerExists(routerUUID))
395 throw new BadRequestException("Router UUID doesn't exist");
396 NeutronRouter target = routerInterface.getRouter(routerUUID);
397 if (input.getSubnetUUID() == null ||
398 input.getPortUUID() == null)
399 throw new BadRequestException("Must specify at subnet id, port id or both");
401 // check that the port is part of the subnet
402 NeutronSubnet targetSubnet = subnetInterface.getSubnet(input.getSubnetUUID());
403 if (targetSubnet == null)
404 throw new BadRequestException("Subnet id doesn't exist");
405 NeutronPort targetPort = portInterface.getPort(input.getPortUUID());
406 if (targetPort == null)
407 throw new BadRequestException("Port id doesn't exist");
408 if (!targetSubnet.getPortsInSubnet().contains(targetPort))
409 throw new BadRequestException("Port id not part of subnet id");
411 if (targetPort.getFixedIPs().size() != 1)
412 throw new BadRequestException("Port id must have a single fixedIP address");
413 if (targetPort.getDeviceID() != null ||
414 targetPort.getDeviceOwner() != null)
415 throw new ResourceConflictException("Target Port already allocated");
416 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
417 if (instances != null) {
418 for (Object instance : instances) {
419 INeutronRouterAware service = (INeutronRouterAware) instance;
420 int status = service.canAttachInterface(target, input);
421 if (status < 200 || status > 299)
422 return Response.status(status).build();
426 //mark the port device id and device owner fields
427 targetPort.setDeviceOwner("network:router_interface");
428 targetPort.setDeviceID(routerUUID);
430 target.addInterface(input.getPortUUID(), input);
431 if (instances != null) {
432 for (Object instance : instances) {
433 INeutronRouterAware service = (INeutronRouterAware) instance;
434 service.neutronRouterInterfaceAttached(target, input);
438 return Response.status(200).entity(input).build();
442 * Removes an interface to a router */
444 @Path("{routerUUID}/remove_router_interface")
446 @Produces({ MediaType.APPLICATION_JSON })
447 @Consumes({ MediaType.APPLICATION_JSON })
448 //@TypeHint(OpenStackRouterInterfaces.class)
450 @ResponseCode(code = 200, condition = "Operation successful"),
451 @ResponseCode(code = 400, condition = "Bad Request"),
452 @ResponseCode(code = 401, condition = "Unauthorized"),
453 @ResponseCode(code = 404, condition = "Not Found"),
454 @ResponseCode(code = 409, condition = "Conflict"),
455 @ResponseCode(code = 501, condition = "Not Implemented") })
456 public Response removeRouterInterface(
457 @PathParam("routerUUID") String routerUUID,
458 NeutronRouter_Interface input
460 INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
461 if (routerInterface == null) {
462 throw new ServiceUnavailableException("Router CRUD Interface "
463 + RestMessages.SERVICEUNAVAILABLE.toString());
465 INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
466 if (portInterface == null) {
467 throw new ServiceUnavailableException("Port CRUD Interface "
468 + RestMessages.SERVICEUNAVAILABLE.toString());
470 INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
471 if (subnetInterface == null) {
472 throw new ServiceUnavailableException("Subnet CRUD Interface "
473 + RestMessages.SERVICEUNAVAILABLE.toString());
476 // verify the router exists
477 if (!routerInterface.routerExists(routerUUID))
478 throw new BadRequestException("Router does not exist");
479 NeutronRouter target = routerInterface.getRouter(routerUUID);
482 * remove by subnet id. Collect information about the impacted router for the response and
483 * remove the port corresponding to the gateway IP address of the subnet
485 if (input.getPortUUID() == null &&
486 input.getSubnetUUID() != null) {
487 NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
489 throw new ResourceNotFoundException("Port UUID not found");
490 input.setPortUUID(port.getID());
491 input.setID(target.getID());
492 input.setTenantID(target.getTenantID());
494 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
495 if (instances != null) {
496 for (Object instance : instances) {
497 INeutronRouterAware service = (INeutronRouterAware) instance;
498 int status = service.canDetachInterface(target, input);
499 if (status < 200 || status > 299)
500 return Response.status(status).build();
504 // reset the port ownership
505 port.setDeviceID(null);
506 port.setDeviceOwner(null);
508 target.removeInterface(input.getPortUUID());
509 if (instances != null) {
510 for (Object instance : instances) {
511 INeutronRouterAware service = (INeutronRouterAware) instance;
512 service.neutronRouterInterfaceDetached(target, input);
515 return Response.status(200).entity(input).build();
519 * remove by port id. collect information about the impacted router for the response
520 * remove the interface and reset the port ownership
522 if (input.getPortUUID() != null &&
523 input.getSubnetUUID() == null) {
524 NeutronRouter_Interface targetInterface = target.getInterfaces().get(input.getPortUUID());
525 if (targetInterface == null) {
526 throw new ResourceNotFoundException("Router interface not found for given Port UUID");
528 input.setSubnetUUID(targetInterface.getSubnetUUID());
529 input.setID(target.getID());
530 input.setTenantID(target.getTenantID());
531 NeutronPort port = portInterface.getPort(input.getPortUUID());
532 port.setDeviceID(null);
533 port.setDeviceOwner(null);
534 target.removeInterface(input.getPortUUID());
535 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
536 for (Object instance : instances) {
537 INeutronRouterAware service = (INeutronRouterAware) instance;
538 service.neutronRouterInterfaceDetached(target, input);
540 return Response.status(200).entity(input).build();
544 * remove by both port and subnet ID. Verify that the first fixed IP of the port is a valid
545 * IP address for the subnet, and then remove the interface, collecting information about the
546 * impacted router for the response and reset port ownership
548 if (input.getPortUUID() != null &&
549 input.getSubnetUUID() != null) {
550 NeutronPort port = portInterface.getPort(input.getPortUUID());
552 throw new ResourceNotFoundException("Port UUID not found");
554 if (port.getFixedIPs() == null) {
555 throw new ResourceNotFoundException("Port UUID has no fixed IPs");
557 NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
558 if (subnet == null) {
559 throw new ResourceNotFoundException("Subnet UUID not found");
561 if (!subnet.isValidIP(port.getFixedIPs().get(0).getIpAddress()))
562 throw new ResourceConflictException("Target Port IP not in Target Subnet");
563 input.setID(target.getID());
564 input.setTenantID(target.getTenantID());
565 Object[] instances = NeutronUtil.getInstances(INeutronRouterAware.class, this);
566 if (instances != null) {
567 for (Object instance : instances) {
568 INeutronRouterAware service = (INeutronRouterAware) instance;
569 service.canDetachInterface(target, input);
572 port.setDeviceID(null);
573 port.setDeviceOwner(null);
574 target.removeInterface(input.getPortUUID());
575 for (Object instance : instances) {
576 INeutronRouterAware service = (INeutronRouterAware) instance;
577 service.neutronRouterInterfaceDetached(target, input);
579 return Response.status(200).entity(input).build();
582 // have to specify either a port ID or a subnet ID
583 throw new BadRequestException("Must specify port id or subnet id or both");