2 * Copyright IBM Corporation, 2013. All rights reserved.
\r
4 * This program and the accompanying materials are made available under the
\r
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
\r
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
\r
9 package org.opendaylight.controller.networkconfig.neutron.northbound;
\r
11 import java.util.ArrayList;
\r
12 import java.util.Iterator;
\r
13 import java.util.List;
\r
14 import javax.ws.rs.Consumes;
\r
15 import javax.ws.rs.DELETE;
\r
16 import javax.ws.rs.GET;
\r
17 import javax.ws.rs.POST;
\r
18 import javax.ws.rs.PUT;
\r
19 import javax.ws.rs.Path;
\r
20 import javax.ws.rs.PathParam;
\r
21 import javax.ws.rs.Produces;
\r
22 import javax.ws.rs.QueryParam;
\r
23 import javax.ws.rs.core.MediaType;
\r
24 import javax.ws.rs.core.Response;
\r
26 import org.codehaus.enunciate.jaxrs.ResponseCode;
\r
27 import org.codehaus.enunciate.jaxrs.StatusCodes;
\r
28 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
\r
29 import org.opendaylight.controller.networkconfig.neutron.INeutronPortAware;
\r
30 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
\r
31 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterAware;
\r
32 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterCRUD;
\r
33 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
\r
34 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
\r
35 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
\r
36 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter;
\r
37 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
\r
38 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
\r
39 import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;
\r
40 import org.opendaylight.controller.northbound.commons.RestMessages;
\r
41 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
\r
42 import org.opendaylight.controller.sal.utils.ServiceHelper;
\r
46 * Open DOVE Northbound REST APIs.<br>
\r
47 * This class provides REST APIs for managing the open DOVE
\r
51 * Authentication scheme : <b>HTTP Basic</b><br>
\r
52 * Authentication realm : <b>opendaylight</b><br>
\r
53 * Transport : <b>HTTP and HTTPS</b><br>
\r
55 * HTTPS Authentication is disabled by default. Administrator can enable it in
\r
56 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
\r
57 * trusted authority.<br>
\r
59 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
\r
64 public class NeutronRoutersNorthbound {
\r
66 private NeutronRouter extractFields(NeutronRouter o, List<String> fields) {
\r
67 return o.extractFields(fields);
\r
71 * Returns a list of all Routers */
\r
74 @Produces({ MediaType.APPLICATION_JSON })
\r
75 //@TypeHint(OpenStackRouters.class)
\r
77 @ResponseCode(code = 200, condition = "Operation successful"),
\r
78 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
79 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
80 public Response listRouters(
\r
82 @QueryParam("fields") List<String> fields,
\r
83 // note: openstack isn't clear about filtering on lists, so we aren't handling them
\r
84 @QueryParam("id") String queryID,
\r
85 @QueryParam("name") String queryName,
\r
86 @QueryParam("admin_state_up") String queryAdminStateUp,
\r
87 @QueryParam("status") String queryStatus,
\r
88 @QueryParam("tenant_id") String queryTenantID,
\r
89 @QueryParam("external_gateway_info") String queryExternalGatewayInfo,
\r
91 @QueryParam("limit") String limit,
\r
92 @QueryParam("marker") String marker,
\r
93 @QueryParam("page_reverse") String pageReverse
\r
94 // sorting not supported
\r
96 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
97 if (routerInterface == null) {
\r
98 throw new ServiceUnavailableException("Router CRUD Interface "
\r
99 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
101 List<NeutronRouter> allRouters = routerInterface.getAllRouters();
\r
102 List<NeutronRouter> ans = new ArrayList<NeutronRouter>();
\r
103 Iterator<NeutronRouter> i = allRouters.iterator();
\r
104 while (i.hasNext()) {
\r
105 NeutronRouter oSS = i.next();
\r
106 if ((queryID == null || queryID.equals(oSS.getID())) &&
\r
107 (queryName == null || queryName.equals(oSS.getName())) &&
\r
108 (queryAdminStateUp == null || queryAdminStateUp.equals(oSS.getAdminStateUp())) &&
\r
109 (queryStatus == null || queryStatus.equals(oSS.getStatus())) &&
\r
110 (queryExternalGatewayInfo == null || queryExternalGatewayInfo.equals(oSS.getExternalGatewayInfo())) &&
\r
111 (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
\r
112 if (fields.size() > 0)
\r
113 ans.add(extractFields(oSS,fields));
\r
118 //TODO: apply pagination to results
\r
119 return Response.status(200).entity(
\r
120 new NeutronRouterRequest(ans)).build();
\r
124 * Returns a specific Router */
\r
126 @Path("{routerUUID}")
\r
128 @Produces({ MediaType.APPLICATION_JSON })
\r
129 //@TypeHint(OpenStackRouters.class)
\r
131 @ResponseCode(code = 200, condition = "Operation successful"),
\r
132 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
133 @ResponseCode(code = 403, condition = "Forbidden"),
\r
134 @ResponseCode(code = 404, condition = "Not Found"),
\r
135 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
136 public Response showRouter(
\r
137 @PathParam("routerUUID") String routerUUID,
\r
139 @QueryParam("fields") List<String> fields) {
\r
140 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
141 if (routerInterface == null) {
\r
142 throw new ServiceUnavailableException("Router CRUD Interface "
\r
143 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
145 if (!routerInterface.routerExists(routerUUID))
\r
146 return Response.status(404).build();
\r
147 if (fields.size() > 0) {
\r
148 NeutronRouter ans = routerInterface.getRouter(routerUUID);
\r
149 return Response.status(200).entity(
\r
150 new NeutronRouterRequest(extractFields(ans, fields))).build();
\r
152 return Response.status(200).entity(
\r
153 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
\r
157 * Creates new Routers */
\r
160 @Produces({ MediaType.APPLICATION_JSON })
\r
161 @Consumes({ MediaType.APPLICATION_JSON })
\r
162 //@TypeHint(OpenStackRouters.class)
\r
164 @ResponseCode(code = 201, condition = "Created"),
\r
165 @ResponseCode(code = 400, condition = "Bad Request"),
\r
166 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
167 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
168 public Response createRouters(final NeutronRouterRequest input) {
\r
169 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
170 if (routerInterface == null) {
\r
171 throw new ServiceUnavailableException("Router CRUD Interface "
\r
172 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
174 INeutronNetworkCRUD networkInterface = NeutronNBInterfaces.getIfNBNetworkCRUD("default", this);
\r
175 if (networkInterface == null) {
\r
176 throw new ServiceUnavailableException("Network CRUD Interface "
\r
177 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
179 if (input.isSingleton()) {
\r
180 NeutronRouter singleton = input.getSingleton();
\r
183 * verify that the router doesn't already exist (issue: is deeper inspection necessary?)
\r
184 * if there is external gateway information provided, verify that the specified network
\r
185 * exists and has been designated as "router:external"
\r
187 if (routerInterface.routerExists(singleton.getID()))
\r
188 return Response.status(400).build();
\r
189 if (singleton.getExternalGatewayInfo() != null) {
\r
190 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
\r
191 if (!networkInterface.networkExists(externNetworkPtr))
\r
192 return Response.status(400).build();
\r
193 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
\r
194 if (!externNetwork.isRouterExternal())
\r
195 return Response.status(400).build();
\r
197 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
198 if (instances != null) {
\r
199 for (Object instance : instances) {
\r
200 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
201 int status = service.canCreateRouter(singleton);
\r
202 if (status < 200 || status > 299)
\r
203 return Response.status(status).build();
\r
208 * add router to the cache
\r
210 routerInterface.addRouter(singleton);
\r
211 if (instances != null) {
\r
212 for (Object instance : instances) {
\r
213 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
214 service.neutronRouterCreated(singleton);
\r
220 * only singleton router creates supported
\r
222 return Response.status(400).build();
\r
224 return Response.status(201).entity(input).build();
\r
228 * Updates a Router */
\r
230 @Path("{routerUUID}")
\r
232 @Produces({ MediaType.APPLICATION_JSON })
\r
233 @Consumes({ MediaType.APPLICATION_JSON })
\r
234 //@TypeHint(OpenStackRouters.class)
\r
236 @ResponseCode(code = 200, condition = "Operation successful"),
\r
237 @ResponseCode(code = 400, condition = "Bad Request"),
\r
238 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
239 @ResponseCode(code = 404, condition = "Not Found"),
\r
240 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
241 public Response updateRouter(
\r
242 @PathParam("routerUUID") String routerUUID,
\r
243 NeutronRouterRequest input
\r
245 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
246 if (routerInterface == null) {
\r
247 throw new ServiceUnavailableException("Router CRUD Interface "
\r
248 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
250 INeutronNetworkCRUD networkInterface = NeutronNBInterfaces.getIfNBNetworkCRUD("default", this);
\r
251 if (networkInterface == null) {
\r
252 throw new ServiceUnavailableException("Network CRUD Interface "
\r
253 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
257 * router has to exist and only a single delta can be supplied
\r
259 if (!routerInterface.routerExists(routerUUID))
\r
260 return Response.status(404).build();
\r
261 if (!input.isSingleton())
\r
262 return Response.status(400).build();
\r
263 NeutronRouter singleton = input.getSingleton();
\r
264 NeutronRouter original = routerInterface.getRouter(routerUUID);
\r
267 * attribute changes blocked by Neutron
\r
269 if (singleton.getID() != null || singleton.getTenantID() != null ||
\r
270 singleton.getStatus() != null)
\r
271 return Response.status(400).build();
\r
273 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
274 if (instances != null) {
\r
275 for (Object instance : instances) {
\r
276 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
277 int status = service.canUpdateRouter(singleton, original);
\r
278 if (status < 200 || status > 299)
\r
279 return Response.status(status).build();
\r
283 * if the external gateway info is being changed, verify that the new network
\r
284 * exists and has been designated as an external network
\r
286 if (singleton.getExternalGatewayInfo() != null) {
\r
287 String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
\r
288 if (!networkInterface.networkExists(externNetworkPtr))
\r
289 return Response.status(400).build();
\r
290 NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
\r
291 if (!externNetwork.isRouterExternal())
\r
292 return Response.status(400).build();
\r
296 * update the router entry and return the modified object
\r
298 routerInterface.updateRouter(routerUUID, singleton);
\r
299 NeutronRouter updatedRouter = routerInterface.getRouter(routerUUID);
\r
300 if (instances != null) {
\r
301 for (Object instance : instances) {
\r
302 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
303 service.neutronRouterUpdated(updatedRouter);
\r
306 return Response.status(200).entity(
\r
307 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
\r
312 * Deletes a Router */
\r
314 @Path("{routerUUID}")
\r
317 @ResponseCode(code = 204, condition = "No Content"),
\r
318 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
319 @ResponseCode(code = 404, condition = "Not Found"),
\r
320 @ResponseCode(code = 409, condition = "Conflict"),
\r
321 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
322 public Response deleteRouter(
\r
323 @PathParam("routerUUID") String routerUUID) {
\r
324 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
325 if (routerInterface == null) {
\r
326 throw new ServiceUnavailableException("Router CRUD Interface "
\r
327 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
331 * verify that the router exists and is not in use before removing it
\r
333 if (!routerInterface.routerExists(routerUUID))
\r
334 return Response.status(404).build();
\r
335 if (routerInterface.routerInUse(routerUUID))
\r
336 return Response.status(409).build();
\r
337 NeutronRouter singleton = routerInterface.getRouter(routerUUID);
\r
338 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
339 if (instances != null) {
\r
340 for (Object instance : instances) {
\r
341 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
342 int status = service.canDeleteRouter(singleton);
\r
343 if (status < 200 || status > 299)
\r
344 return Response.status(status).build();
\r
347 routerInterface.removeRouter(routerUUID);
\r
348 if (instances != null) {
\r
349 for (Object instance : instances) {
\r
350 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
351 service.neutronRouterDeleted(singleton);
\r
354 return Response.status(204).build();
\r
358 * Adds an interface to a router */
\r
360 @Path("{routerUUID}/add_router_interface")
\r
362 @Produces({ MediaType.APPLICATION_JSON })
\r
363 @Consumes({ MediaType.APPLICATION_JSON })
\r
364 //@TypeHint(OpenStackRouterInterfaces.class)
\r
366 @ResponseCode(code = 200, condition = "Operation successful"),
\r
367 @ResponseCode(code = 400, condition = "Bad Request"),
\r
368 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
369 @ResponseCode(code = 404, condition = "Not Found"),
\r
370 @ResponseCode(code = 409, condition = "Conflict"),
\r
371 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
372 public Response addRouterInterface(
\r
373 @PathParam("routerUUID") String routerUUID,
\r
374 NeutronRouter_Interface input
\r
376 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
377 if (routerInterface == null) {
\r
378 throw new ServiceUnavailableException("Router CRUD Interface "
\r
379 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
381 INeutronPortCRUD portInterface = NeutronNBInterfaces.getIfNBPortCRUD("default",this);
\r
382 if (portInterface == null) {
\r
383 throw new ServiceUnavailableException("Port CRUD Interface "
\r
384 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
386 INeutronSubnetCRUD subnetInterface = NeutronNBInterfaces.getIfNBSubnetCRUD("default",this);
\r
387 if (subnetInterface == null) {
\r
388 throw new ServiceUnavailableException("Subnet CRUD Interface "
\r
389 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
393 * While the Neutron specification says that the router has to exist and the input can only specify either a subnet id
\r
394 * 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
\r
396 if (!routerInterface.routerExists(routerUUID))
\r
397 return Response.status(400).build();
\r
398 NeutronRouter target = routerInterface.getRouter(routerUUID);
\r
399 if (input.getSubnetUUID() == null ||
\r
400 input.getPortUUID() == null)
\r
401 return Response.status(400).build();
\r
403 // check that the port is part of the subnet
\r
404 NeutronSubnet targetSubnet = subnetInterface.getSubnet(input.getSubnetUUID());
\r
405 if (targetSubnet == null)
\r
406 return Response.status(400).build();
\r
407 NeutronPort targetPort = portInterface.getPort(input.getPortUUID());
\r
408 if (targetPort == null)
\r
409 return Response.status(400).build();
\r
410 if (!targetSubnet.getPortsInSubnet().contains(targetPort))
\r
411 return Response.status(400).build();
\r
413 if (targetPort.getFixedIPs().size() != 1)
\r
414 return Response.status(400).build();
\r
415 if (targetPort.getDeviceID() != null ||
\r
416 targetPort.getDeviceOwner() != null)
\r
417 return Response.status(409).build();
\r
419 //mark the port device id and device owner fields
\r
420 targetPort.setDeviceOwner("network:router_interface");
\r
421 targetPort.setDeviceID(routerUUID);
\r
423 target.addInterface(input.getPortUUID(), input);
\r
424 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
425 if (instances != null) {
\r
426 for (Object instance : instances) {
\r
427 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
428 service.neutronRouterInterfaceAttached(target, input);
\r
432 return Response.status(200).entity(input).build();
\r
436 * Removes an interface to a router */
\r
438 @Path("{routerUUID}/remove_router_interface")
\r
440 @Produces({ MediaType.APPLICATION_JSON })
\r
441 @Consumes({ MediaType.APPLICATION_JSON })
\r
442 //@TypeHint(OpenStackRouterInterfaces.class)
\r
444 @ResponseCode(code = 200, condition = "Operation successful"),
\r
445 @ResponseCode(code = 400, condition = "Bad Request"),
\r
446 @ResponseCode(code = 401, condition = "Unauthorized"),
\r
447 @ResponseCode(code = 404, condition = "Not Found"),
\r
448 @ResponseCode(code = 409, condition = "Conflict"),
\r
449 @ResponseCode(code = 501, condition = "Not Implemented") })
\r
450 public Response removeRouterInterface(
\r
451 @PathParam("routerUUID") String routerUUID,
\r
452 NeutronRouter_Interface input
\r
454 INeutronRouterCRUD routerInterface = NeutronNBInterfaces.getIfNBRouterCRUD("default",this);
\r
455 if (routerInterface == null) {
\r
456 throw new ServiceUnavailableException("Router CRUD Interface "
\r
457 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
459 INeutronPortCRUD portInterface = NeutronNBInterfaces.getIfNBPortCRUD("default",this);
\r
460 if (portInterface == null) {
\r
461 throw new ServiceUnavailableException("Port CRUD Interface "
\r
462 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
464 INeutronSubnetCRUD subnetInterface = NeutronNBInterfaces.getIfNBSubnetCRUD("default",this);
\r
465 if (subnetInterface == null) {
\r
466 throw new ServiceUnavailableException("Subnet CRUD Interface "
\r
467 + RestMessages.SERVICEUNAVAILABLE.toString());
\r
470 // verify the router exists
\r
471 if (!routerInterface.routerExists(routerUUID))
\r
472 return Response.status(400).build();
\r
473 NeutronRouter target = routerInterface.getRouter(routerUUID);
\r
476 * remove by subnet id. Collect information about the impacted router for the response and
\r
477 * remove the port corresponding to the gateway IP address of the subnet
\r
479 if (input.getPortUUID() == null &&
\r
480 input.getSubnetUUID() != null) {
\r
481 NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
\r
483 return Response.status(404).build();
\r
484 input.setPortUUID(port.getID());
\r
485 input.setID(target.getID());
\r
486 input.setTenantID(target.getTenantID());
\r
488 // reset the port ownership
\r
489 port.setDeviceID(null);
\r
490 port.setDeviceOwner(null);
\r
492 target.removeInterface(input.getPortUUID());
\r
493 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
494 if (instances != null) {
\r
495 for (Object instance : instances) {
\r
496 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
497 service.neutronRouterInterfaceDetached(target, input);
\r
500 return Response.status(200).entity(input).build();
\r
504 * remove by port id. collect information about the impacted router for the response
\r
505 * remove the interface and reset the port ownership
\r
507 if (input.getPortUUID() != null &&
\r
508 input.getSubnetUUID() == null) {
\r
509 NeutronRouter_Interface targetInterface = target.getInterfaces().get(input.getPortUUID());
\r
510 input.setSubnetUUID(targetInterface.getSubnetUUID());
\r
511 input.setID(target.getID());
\r
512 input.setTenantID(target.getTenantID());
\r
513 NeutronPort port = portInterface.getPort(input.getPortUUID());
\r
514 port.setDeviceID(null);
\r
515 port.setDeviceOwner(null);
\r
516 target.removeInterface(input.getPortUUID());
\r
517 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
518 for (Object instance : instances) {
\r
519 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
520 service.neutronRouterInterfaceDetached(target, input);
\r
522 return Response.status(200).entity(input).build();
\r
526 * remove by both port and subnet ID. Verify that the first fixed IP of the port is a valid
\r
527 * IP address for the subnet, and then remove the interface, collecting information about the
\r
528 * impacted router for the response and reset port ownership
\r
530 if (input.getPortUUID() != null &&
\r
531 input.getSubnetUUID() != null) {
\r
532 NeutronPort port = portInterface.getPort(input.getPortUUID());
\r
533 NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
\r
534 if (!subnet.isValidIP(port.getFixedIPs().get(0).getIpAddress()))
\r
535 return Response.status(409).build();
\r
536 input.setID(target.getID());
\r
537 input.setTenantID(target.getTenantID());
\r
538 port.setDeviceID(null);
\r
539 port.setDeviceOwner(null);
\r
540 target.removeInterface(input.getPortUUID());
\r
541 Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
\r
542 for (Object instance : instances) {
\r
543 INeutronRouterAware service = (INeutronRouterAware) instance;
\r
544 service.neutronRouterInterfaceDetached(target, input);
\r
546 return Response.status(200).entity(input).build();
\r
549 // have to specify either a port ID or a subnet ID
\r
550 return Response.status(400).build();
\r