Bring features/neutron into the same parent dir as everything else
[controller.git] / opendaylight / networkconfiguration / neutron / northbound / src / main / java / org / opendaylight / controller / networkconfig / neutron / northbound / NeutronRoutersNorthbound.java
1 /*
2  * Copyright IBM Corporation, 2013.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.networkconfig.neutron.northbound;
10
11 import java.util.ArrayList;
12 import java.util.Iterator;
13 import java.util.List;
14
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;
26
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;
40
41
42 /**
43  * Neutron Northbound REST APIs.<br>
44  * This class provides REST APIs for managing neutron routers
45  *
46  * <br>
47  * <br>
48  * Authentication scheme : <b>HTTP Basic</b><br>
49  * Authentication realm : <b>opendaylight</b><br>
50  * Transport : <b>HTTP and HTTPS</b><br>
51  * <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>
55  * More info :
56  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
57  *
58  */
59
60 @Path("/routers")
61 public class NeutronRoutersNorthbound {
62
63     private NeutronRouter extractFields(NeutronRouter o, List<String> fields) {
64         return o.extractFields(fields);
65     }
66
67     /**
68      * Returns a list of all Routers */
69
70     @GET
71     @Produces({ MediaType.APPLICATION_JSON })
72     //@TypeHint(OpenStackRouters.class)
73     @StatusCodes({
74             @ResponseCode(code = 200, condition = "Operation successful"),
75             @ResponseCode(code = 401, condition = "Unauthorized"),
76             @ResponseCode(code = 501, condition = "Not Implemented") })
77     public Response listRouters(
78             // return fields
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,
87             // pagination
88             @QueryParam("limit") String limit,
89             @QueryParam("marker") String marker,
90             @QueryParam("page_reverse") String pageReverse
91             // sorting not supported
92             ) {
93         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
94         if (routerInterface == null) {
95             throw new ServiceUnavailableException("Router CRUD Interface "
96                     + RestMessages.SERVICEUNAVAILABLE.toString());
97         }
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));
111                 else
112                     ans.add(oSS);
113             }
114         }
115         //TODO: apply pagination to results
116         return Response.status(200).entity(
117                 new NeutronRouterRequest(ans)).build();
118     }
119
120     /**
121      * Returns a specific Router */
122
123     @Path("{routerUUID}")
124     @GET
125     @Produces({ MediaType.APPLICATION_JSON })
126     //@TypeHint(OpenStackRouters.class)
127     @StatusCodes({
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,
135             // return fields
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());
141         }
142         if (!routerInterface.routerExists(routerUUID)) {
143             throw new ResourceNotFoundException("Router UUID not found");
144         }
145         if (fields.size() > 0) {
146             NeutronRouter ans = routerInterface.getRouter(routerUUID);
147             return Response.status(200).entity(
148                     new NeutronRouterRequest(extractFields(ans, fields))).build();
149         } else
150             return Response.status(200).entity(
151                     new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
152     }
153
154     /**
155      * Creates new Routers */
156
157     @POST
158     @Produces({ MediaType.APPLICATION_JSON })
159     @Consumes({ MediaType.APPLICATION_JSON })
160     //@TypeHint(OpenStackRouters.class)
161     @StatusCodes({
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());
171         }
172         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
173         if (networkInterface == null) {
174             throw new ServiceUnavailableException("Network CRUD Interface "
175                     + RestMessages.SERVICEUNAVAILABLE.toString());
176         }
177         if (input.isSingleton()) {
178             NeutronRouter singleton = input.getSingleton();
179
180             /*
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"
184              */
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");
194             }
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();
202                 }
203             }
204
205             /*
206              * add router to the cache
207              */
208             routerInterface.addRouter(singleton);
209             if (instances != null) {
210                 for (Object instance : instances) {
211                     INeutronRouterAware service = (INeutronRouterAware) instance;
212                     service.neutronRouterCreated(singleton);
213                 }
214             }
215         } else {
216
217             /*
218              * only singleton router creates supported
219              */
220             throw new BadRequestException("Only singleton router creates supported");
221         }
222         return Response.status(201).entity(input).build();
223     }
224
225     /**
226      * Updates a Router */
227
228     @Path("{routerUUID}")
229     @PUT
230     @Produces({ MediaType.APPLICATION_JSON })
231     @Consumes({ MediaType.APPLICATION_JSON })
232     //@TypeHint(OpenStackRouters.class)
233     @StatusCodes({
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
242             ) {
243         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
244         if (routerInterface == null) {
245             throw new ServiceUnavailableException("Router CRUD Interface "
246                     + RestMessages.SERVICEUNAVAILABLE.toString());
247         }
248         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
249         if (networkInterface == null) {
250             throw new ServiceUnavailableException("Network CRUD Interface "
251                     + RestMessages.SERVICEUNAVAILABLE.toString());
252         }
253
254         /*
255          * router has to exist and only a single delta can be supplied
256          */
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);
263
264         /*
265          * attribute changes blocked by Neutron
266          */
267         if (singleton.getID() != null || singleton.getTenantID() != null ||
268                 singleton.getStatus() != null)
269             throw new BadRequestException("Request attribute change not allowed");
270
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();
278             }
279         }
280         /*
281          * if the external gateway info is being changed, verify that the new network
282          * exists and has been designated as an external network
283          */
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");
291         }
292
293         /*
294          * update the router entry and return the modified object
295          */
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);
302             }
303         }
304         return Response.status(200).entity(
305                 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
306
307     }
308
309     /**
310      * Deletes a Router */
311
312     @Path("{routerUUID}")
313     @DELETE
314     @StatusCodes({
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());
326         }
327
328         /*
329          * verify that the router exists and is not in use before removing it
330          */
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();
343             }
344         }
345         routerInterface.removeRouter(routerUUID);
346         if (instances != null) {
347             for (Object instance : instances) {
348                 INeutronRouterAware service = (INeutronRouterAware) instance;
349                 service.neutronRouterDeleted(singleton);
350             }
351         }
352         return Response.status(204).build();
353     }
354
355     /**
356      * Adds an interface to a router */
357
358     @Path("{routerUUID}/add_router_interface")
359     @PUT
360     @Produces({ MediaType.APPLICATION_JSON })
361     @Consumes({ MediaType.APPLICATION_JSON })
362     //@TypeHint(OpenStackRouterInterfaces.class)
363     @StatusCodes({
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
373             ) {
374         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
375         if (routerInterface == null) {
376             throw new ServiceUnavailableException("Router CRUD Interface "
377                     + RestMessages.SERVICEUNAVAILABLE.toString());
378         }
379         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
380         if (portInterface == null) {
381             throw new ServiceUnavailableException("Port CRUD Interface "
382                     + RestMessages.SERVICEUNAVAILABLE.toString());
383         }
384         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
385         if (subnetInterface == null) {
386             throw new ServiceUnavailableException("Subnet CRUD Interface "
387                     + RestMessages.SERVICEUNAVAILABLE.toString());
388         }
389
390         /*
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
393          */
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");
400
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");
410
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();
423             }
424         }
425
426         //mark the port device id and device owner fields
427         targetPort.setDeviceOwner("network:router_interface");
428         targetPort.setDeviceID(routerUUID);
429
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);
435             }
436         }
437
438         return Response.status(200).entity(input).build();
439     }
440
441     /**
442      * Removes an interface to a router */
443
444     @Path("{routerUUID}/remove_router_interface")
445     @PUT
446     @Produces({ MediaType.APPLICATION_JSON })
447     @Consumes({ MediaType.APPLICATION_JSON })
448     //@TypeHint(OpenStackRouterInterfaces.class)
449     @StatusCodes({
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
459             ) {
460         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
461         if (routerInterface == null) {
462             throw new ServiceUnavailableException("Router CRUD Interface "
463                     + RestMessages.SERVICEUNAVAILABLE.toString());
464         }
465         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
466         if (portInterface == null) {
467             throw new ServiceUnavailableException("Port CRUD Interface "
468                     + RestMessages.SERVICEUNAVAILABLE.toString());
469         }
470         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
471         if (subnetInterface == null) {
472             throw new ServiceUnavailableException("Subnet CRUD Interface "
473                     + RestMessages.SERVICEUNAVAILABLE.toString());
474         }
475
476         // verify the router exists
477         if (!routerInterface.routerExists(routerUUID))
478             throw new BadRequestException("Router does not exist");
479         NeutronRouter target = routerInterface.getRouter(routerUUID);
480
481         /*
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
484          */
485         if (input.getPortUUID() == null &&
486                 input.getSubnetUUID() != null) {
487             NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
488             if (port == null)
489                 throw new ResourceNotFoundException("Port UUID not found");
490             input.setPortUUID(port.getID());
491             input.setID(target.getID());
492             input.setTenantID(target.getTenantID());
493
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();
501                 }
502             }
503
504             // reset the port ownership
505             port.setDeviceID(null);
506             port.setDeviceOwner(null);
507
508             target.removeInterface(input.getPortUUID());
509             if (instances != null) {
510                 for (Object instance : instances) {
511                     INeutronRouterAware service = (INeutronRouterAware) instance;
512                     service.neutronRouterInterfaceDetached(target, input);
513                 }
514             }
515             return Response.status(200).entity(input).build();
516         }
517
518         /*
519          * remove by port id. collect information about the impacted router for the response
520          * remove the interface and reset the port ownership
521          */
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");
527             }
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);
539             }
540             return Response.status(200).entity(input).build();
541         }
542
543         /*
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
547          */
548         if (input.getPortUUID() != null &&
549                 input.getSubnetUUID() != null) {
550             NeutronPort port = portInterface.getPort(input.getPortUUID());
551             if (port == null) {
552                 throw new ResourceNotFoundException("Port UUID not found");
553             }
554             if (port.getFixedIPs() == null) {
555                 throw new ResourceNotFoundException("Port UUID has no fixed IPs");
556             }
557             NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
558             if (subnet == null) {
559                 throw new ResourceNotFoundException("Subnet UUID not found");
560             }
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);
570                 }
571             }
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);
578             }
579             return Response.status(200).entity(input).build();
580         }
581
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");
584     }
585 }