Merge "Fixed for bug : 1171 - issue while creating subnet"
[controller.git] / opendaylight / northbound / networkconfiguration / neutron / 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 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;
25
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;
45
46
47 /**
48  * Neutron Northbound REST APIs.<br>
49  * This class provides REST APIs for managing neutron routers
50  *
51  * <br>
52  * <br>
53  * Authentication scheme : <b>HTTP Basic</b><br>
54  * Authentication realm : <b>opendaylight</b><br>
55  * Transport : <b>HTTP and HTTPS</b><br>
56  * <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>
60  * More info :
61  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
62  *
63  */
64
65 @Path("/routers")
66 public class NeutronRoutersNorthbound {
67
68     private NeutronRouter extractFields(NeutronRouter o, List<String> fields) {
69         return o.extractFields(fields);
70     }
71
72     /**
73      * Returns a list of all Routers */
74
75     @GET
76     @Produces({ MediaType.APPLICATION_JSON })
77     //@TypeHint(OpenStackRouters.class)
78     @StatusCodes({
79             @ResponseCode(code = 200, condition = "Operation successful"),
80             @ResponseCode(code = 401, condition = "Unauthorized"),
81             @ResponseCode(code = 501, condition = "Not Implemented") })
82     public Response listRouters(
83             // return fields
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,
92             // pagination
93             @QueryParam("limit") String limit,
94             @QueryParam("marker") String marker,
95             @QueryParam("page_reverse") String pageReverse
96             // sorting not supported
97             ) {
98         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
99         if (routerInterface == null) {
100             throw new ServiceUnavailableException("Router CRUD Interface "
101                     + RestMessages.SERVICEUNAVAILABLE.toString());
102         }
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));
116                 else
117                     ans.add(oSS);
118             }
119         }
120         //TODO: apply pagination to results
121         return Response.status(200).entity(
122                 new NeutronRouterRequest(ans)).build();
123     }
124
125     /**
126      * Returns a specific Router */
127
128     @Path("{routerUUID}")
129     @GET
130     @Produces({ MediaType.APPLICATION_JSON })
131     //@TypeHint(OpenStackRouters.class)
132     @StatusCodes({
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,
140             // return fields
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());
146         }
147         if (!routerInterface.routerExists(routerUUID)) {
148             throw new ResourceNotFoundException("Router UUID not found");
149         }
150         if (fields.size() > 0) {
151             NeutronRouter ans = routerInterface.getRouter(routerUUID);
152             return Response.status(200).entity(
153                     new NeutronRouterRequest(extractFields(ans, fields))).build();
154         } else
155             return Response.status(200).entity(
156                     new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
157     }
158
159     /**
160      * Creates new Routers */
161
162     @POST
163     @Produces({ MediaType.APPLICATION_JSON })
164     @Consumes({ MediaType.APPLICATION_JSON })
165     //@TypeHint(OpenStackRouters.class)
166     @StatusCodes({
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());
176         }
177         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
178         if (networkInterface == null) {
179             throw new ServiceUnavailableException("Network CRUD Interface "
180                     + RestMessages.SERVICEUNAVAILABLE.toString());
181         }
182         if (input.isSingleton()) {
183             NeutronRouter singleton = input.getSingleton();
184
185             /*
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"
189              */
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");
199             }
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();
207                 }
208             }
209
210             /*
211              * add router to the cache
212              */
213             routerInterface.addRouter(singleton);
214             if (instances != null) {
215                 for (Object instance : instances) {
216                     INeutronRouterAware service = (INeutronRouterAware) instance;
217                     service.neutronRouterCreated(singleton);
218                 }
219             }
220         } else {
221
222             /*
223              * only singleton router creates supported
224              */
225             throw new BadRequestException("Only singleton router creates supported");
226         }
227         return Response.status(201).entity(input).build();
228     }
229
230     /**
231      * Updates a Router */
232
233     @Path("{routerUUID}")
234     @PUT
235     @Produces({ MediaType.APPLICATION_JSON })
236     @Consumes({ MediaType.APPLICATION_JSON })
237     //@TypeHint(OpenStackRouters.class)
238     @StatusCodes({
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
247             ) {
248         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
249         if (routerInterface == null) {
250             throw new ServiceUnavailableException("Router CRUD Interface "
251                     + RestMessages.SERVICEUNAVAILABLE.toString());
252         }
253         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
254         if (networkInterface == null) {
255             throw new ServiceUnavailableException("Network CRUD Interface "
256                     + RestMessages.SERVICEUNAVAILABLE.toString());
257         }
258
259         /*
260          * router has to exist and only a single delta can be supplied
261          */
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);
268
269         /*
270          * attribute changes blocked by Neutron
271          */
272         if (singleton.getID() != null || singleton.getTenantID() != null ||
273                 singleton.getStatus() != null)
274             throw new BadRequestException("Request attribute change not allowed");
275
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();
283             }
284         }
285         /*
286          * if the external gateway info is being changed, verify that the new network
287          * exists and has been designated as an external network
288          */
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");
296         }
297
298         /*
299          * update the router entry and return the modified object
300          */
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);
307             }
308         }
309         return Response.status(200).entity(
310                 new NeutronRouterRequest(routerInterface.getRouter(routerUUID))).build();
311
312     }
313
314     /**
315      * Deletes a Router */
316
317     @Path("{routerUUID}")
318     @DELETE
319     @StatusCodes({
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());
331         }
332
333         /*
334          * verify that the router exists and is not in use before removing it
335          */
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();
348             }
349         }
350         routerInterface.removeRouter(routerUUID);
351         if (instances != null) {
352             for (Object instance : instances) {
353                 INeutronRouterAware service = (INeutronRouterAware) instance;
354                 service.neutronRouterDeleted(singleton);
355             }
356         }
357         return Response.status(204).build();
358     }
359
360     /**
361      * Adds an interface to a router */
362
363     @Path("{routerUUID}/add_router_interface")
364     @PUT
365     @Produces({ MediaType.APPLICATION_JSON })
366     @Consumes({ MediaType.APPLICATION_JSON })
367     //@TypeHint(OpenStackRouterInterfaces.class)
368     @StatusCodes({
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
378             ) {
379         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
380         if (routerInterface == null) {
381             throw new ServiceUnavailableException("Router CRUD Interface "
382                     + RestMessages.SERVICEUNAVAILABLE.toString());
383         }
384         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
385         if (portInterface == null) {
386             throw new ServiceUnavailableException("Port CRUD Interface "
387                     + RestMessages.SERVICEUNAVAILABLE.toString());
388         }
389         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
390         if (subnetInterface == null) {
391             throw new ServiceUnavailableException("Subnet CRUD Interface "
392                     + RestMessages.SERVICEUNAVAILABLE.toString());
393         }
394
395         /*
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
398          */
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");
405
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");
415
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();
428             }
429         }
430
431         //mark the port device id and device owner fields
432         targetPort.setDeviceOwner("network:router_interface");
433         targetPort.setDeviceID(routerUUID);
434
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);
440             }
441         }
442
443         return Response.status(200).entity(input).build();
444     }
445
446     /**
447      * Removes an interface to a router */
448
449     @Path("{routerUUID}/remove_router_interface")
450     @PUT
451     @Produces({ MediaType.APPLICATION_JSON })
452     @Consumes({ MediaType.APPLICATION_JSON })
453     //@TypeHint(OpenStackRouterInterfaces.class)
454     @StatusCodes({
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
464             ) {
465         INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this);
466         if (routerInterface == null) {
467             throw new ServiceUnavailableException("Router CRUD Interface "
468                     + RestMessages.SERVICEUNAVAILABLE.toString());
469         }
470         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
471         if (portInterface == null) {
472             throw new ServiceUnavailableException("Port CRUD Interface "
473                     + RestMessages.SERVICEUNAVAILABLE.toString());
474         }
475         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
476         if (subnetInterface == null) {
477             throw new ServiceUnavailableException("Subnet CRUD Interface "
478                     + RestMessages.SERVICEUNAVAILABLE.toString());
479         }
480
481         // verify the router exists
482         if (!routerInterface.routerExists(routerUUID))
483             throw new BadRequestException("Router does not exist");
484         NeutronRouter target = routerInterface.getRouter(routerUUID);
485
486         /*
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
489          */
490         if (input.getPortUUID() == null &&
491                 input.getSubnetUUID() != null) {
492             NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
493             if (port == null)
494                 throw new ResourceNotFoundException("Port UUID not found");
495             input.setPortUUID(port.getID());
496             input.setID(target.getID());
497             input.setTenantID(target.getTenantID());
498
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();
506                 }
507             }
508
509             // reset the port ownership
510             port.setDeviceID(null);
511             port.setDeviceOwner(null);
512
513             target.removeInterface(input.getPortUUID());
514             if (instances != null) {
515                 for (Object instance : instances) {
516                     INeutronRouterAware service = (INeutronRouterAware) instance;
517                     service.neutronRouterInterfaceDetached(target, input);
518                 }
519             }
520             return Response.status(200).entity(input).build();
521         }
522
523         /*
524          * remove by port id. collect information about the impacted router for the response
525          * remove the interface and reset the port ownership
526          */
527         if (input.getPortUUID() != null &&
528                 input.getSubnetUUID() == null) {
529             NeutronRouter_Interface targetInterface = target.getInterfaces().get(input.getPortUUID());
530             input.setSubnetUUID(targetInterface.getSubnetUUID());
531             input.setID(target.getID());
532             input.setTenantID(target.getTenantID());
533             NeutronPort port = portInterface.getPort(input.getPortUUID());
534             port.setDeviceID(null);
535             port.setDeviceOwner(null);
536             target.removeInterface(input.getPortUUID());
537             Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
538             for (Object instance : instances) {
539                 INeutronRouterAware service = (INeutronRouterAware) instance;
540                 service.neutronRouterInterfaceDetached(target, input);
541             }
542             return Response.status(200).entity(input).build();
543         }
544
545         /*
546          * remove by both port and subnet ID.  Verify that the first fixed IP of the port is a valid
547          * IP address for the subnet, and then remove the interface, collecting information about the
548          * impacted router for the response and reset port ownership
549          */
550         if (input.getPortUUID() != null &&
551                 input.getSubnetUUID() != null) {
552             NeutronPort port = portInterface.getPort(input.getPortUUID());
553             if (port == null) {
554                 throw new ResourceNotFoundException("Port UUID not found");
555             }
556             if (port.getFixedIPs() == null) {
557                 throw new ResourceNotFoundException("Port UUID jas no fixed IPs");
558             }
559             NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
560             if (subnet == null) {
561                 throw new ResourceNotFoundException("Subnet UUID not found");
562             }
563             if (!subnet.isValidIP(port.getFixedIPs().get(0).getIpAddress()))
564                 throw new ResourceConflictException("Target Port IP not in Target Subnet");
565             input.setID(target.getID());
566             input.setTenantID(target.getTenantID());
567             Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
568             if (instances != null) {
569                 for (Object instance : instances) {
570                     INeutronRouterAware service = (INeutronRouterAware) instance;
571                     service.canDetachInterface(target, input);
572                 }
573             }
574             port.setDeviceID(null);
575             port.setDeviceOwner(null);
576             target.removeInterface(input.getPortUUID());
577             for (Object instance : instances) {
578                 INeutronRouterAware service = (INeutronRouterAware) instance;
579                 service.neutronRouterInterfaceDetached(target, input);
580             }
581             return Response.status(200).entity(input).build();
582         }
583
584         // have to specify either a port ID or a subnet ID
585         throw new BadRequestException("Must specify port id or subnet id or both");
586     }
587 }