Merge "Fixed for bug 1168 : Issue while update subnet"
[controller.git] / opendaylight / northbound / networkconfiguration / neutron / src / main / java / org / opendaylight / controller / networkconfig / neutron / northbound / NeutronSubnetsNorthbound.java
1 /*
2  * Copyright IBM Corporation and others, 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
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16
17 import javax.ws.rs.Consumes;
18 import javax.ws.rs.DELETE;
19 import javax.ws.rs.DefaultValue;
20 import javax.ws.rs.GET;
21 import javax.ws.rs.POST;
22 import javax.ws.rs.PUT;
23 import javax.ws.rs.Path;
24 import javax.ws.rs.PathParam;
25 import javax.ws.rs.Produces;
26 import javax.ws.rs.QueryParam;
27 import javax.ws.rs.core.Context;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.UriInfo;
31
32 import org.codehaus.enunciate.jaxrs.ResponseCode;
33 import org.codehaus.enunciate.jaxrs.StatusCodes;
34 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
35 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetAware;
36 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
37 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
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.InternalServerErrorException;
44 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
45 import org.opendaylight.controller.sal.utils.ServiceHelper;
46
47 /**
48  * Neutron Northbound REST APIs for Subnets.<br>
49  * This class provides REST APIs for managing neutron Subnets
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("/subnets")
66 public class NeutronSubnetsNorthbound {
67
68     private NeutronSubnet extractFields(NeutronSubnet o, List<String> fields) {
69         return o.extractFields(fields);
70     }
71
72     @Context
73     UriInfo uriInfo;
74
75     /**
76      * Returns a list of all Subnets */
77     @GET
78     @Produces({ MediaType.APPLICATION_JSON })
79     //@TypeHint(OpenStackSubnets.class)
80     @StatusCodes({
81             @ResponseCode(code = 200, condition = "Operation successful"),
82             @ResponseCode(code = 401, condition = "Unauthorized"),
83             @ResponseCode(code = 501, condition = "Not Implemented") })
84     public Response listSubnets(
85             // return fields
86             @QueryParam("fields") List<String> fields,
87             // note: openstack isn't clear about filtering on lists, so we aren't handling them
88             @QueryParam("id") String queryID,
89             @QueryParam("network_id") String queryNetworkID,
90             @QueryParam("name") String queryName,
91             @QueryParam("ip_version") String queryIPVersion,
92             @QueryParam("cidr") String queryCIDR,
93             @QueryParam("gateway_ip") String queryGatewayIP,
94             @QueryParam("enable_dhcp") String queryEnableDHCP,
95             @QueryParam("tenant_id") String queryTenantID,
96             @QueryParam("ipv6_address_mode") String queryIpV6AddressMode,
97             @QueryParam("ipv6_ra_mode") String queryIpV6RaMode,
98             // linkTitle
99             @QueryParam("limit") Integer limit,
100             @QueryParam("marker") String marker,
101             @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
102             // sorting not supported
103             ) {
104         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
105         if (subnetInterface == null) {
106             throw new ServiceUnavailableException("Subnet CRUD Interface "
107                     + RestMessages.SERVICEUNAVAILABLE.toString());
108         }
109         List<NeutronSubnet> allNetworks = subnetInterface.getAllSubnets();
110         List<NeutronSubnet> ans = new ArrayList<NeutronSubnet>();
111         Iterator<NeutronSubnet> i = allNetworks.iterator();
112         while (i.hasNext()) {
113             NeutronSubnet oSS = i.next();
114             if ((queryID == null || queryID.equals(oSS.getID())) &&
115                     (queryNetworkID == null || queryNetworkID.equals(oSS.getNetworkUUID())) &&
116                     (queryName == null || queryName.equals(oSS.getName())) &&
117                     (queryIPVersion == null || queryIPVersion.equals(oSS.getIpVersion())) &&
118                     (queryCIDR == null || queryCIDR.equals(oSS.getCidr())) &&
119                     (queryGatewayIP == null || queryGatewayIP.equals(oSS.getGatewayIP())) &&
120                     (queryEnableDHCP == null || queryEnableDHCP.equals(oSS.getEnableDHCP())) &&
121                     (queryTenantID == null || queryTenantID.equals(oSS.getTenantID())) &&
122                     (queryIpV6AddressMode == null || queryIpV6AddressMode.equals(oSS.getIpV6AddressMode())) &&
123                     (queryIpV6RaMode == null || queryIpV6RaMode.equals(oSS.getIpV6RaMode()))){
124                 if (fields.size() > 0) {
125                     ans.add(extractFields(oSS,fields));
126                 } else {
127                     ans.add(oSS);
128                 }
129             }
130         }
131
132         if (limit != null && ans.size() > 1) {
133             // Return a paginated request
134             NeutronSubnetRequest request = (NeutronSubnetRequest) PaginatedRequestFactory.createRequest(limit,
135                     marker, pageReverse, uriInfo, ans, NeutronSubnet.class);
136             return Response.status(200).entity(request).build();
137         }
138
139         return Response.status(200).entity(
140                 new NeutronSubnetRequest(ans)).build();
141     }
142
143     /**
144      * Returns a specific Subnet */
145
146     @Path("{subnetUUID}")
147     @GET
148     @Produces({ MediaType.APPLICATION_JSON })
149     //@TypeHint(OpenStackSubnets.class)
150     @StatusCodes({
151             @ResponseCode(code = 200, condition = "Operation successful"),
152             @ResponseCode(code = 401, condition = "Unauthorized"),
153             @ResponseCode(code = 404, condition = "Not Found"),
154             @ResponseCode(code = 501, condition = "Not Implemented") })
155     public Response showSubnet(
156             @PathParam("subnetUUID") String subnetUUID,
157             // return fields
158             @QueryParam("fields") List<String> fields) {
159         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
160         if (subnetInterface == null) {
161             throw new ServiceUnavailableException("Subnet CRUD Interface "
162                     + RestMessages.SERVICEUNAVAILABLE.toString());
163         }
164         if (!subnetInterface.subnetExists(subnetUUID)) {
165             throw new ResourceNotFoundException("subnet UUID does not exist.");
166         }
167         if (fields.size() > 0) {
168             NeutronSubnet ans = subnetInterface.getSubnet(subnetUUID);
169             return Response.status(200).entity(
170                     new NeutronSubnetRequest(extractFields(ans, fields))).build();
171         } else {
172             return Response.status(200).entity(
173                     new NeutronSubnetRequest(subnetInterface.getSubnet(subnetUUID))).build();
174         }
175     }
176
177     /**
178      * Creates new Subnets */
179
180     @POST
181     @Produces({ MediaType.APPLICATION_JSON })
182     @Consumes({ MediaType.APPLICATION_JSON })
183     //@TypeHint(OpenStackSubnets.class)
184     @StatusCodes({
185             @ResponseCode(code = 201, condition = "Created"),
186             @ResponseCode(code = 400, condition = "Bad Request"),
187             @ResponseCode(code = 401, condition = "Unauthorized"),
188             @ResponseCode(code = 403, condition = "Forbidden"),
189             @ResponseCode(code = 404, condition = "Not Found"),
190             @ResponseCode(code = 409, condition = "Conflict"),
191             @ResponseCode(code = 501, condition = "Not Implemented") })
192     public Response createSubnets(final NeutronSubnetRequest input) {
193         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
194         if (subnetInterface == null) {
195             throw new ServiceUnavailableException("Subnet CRUD Interface "
196                     + RestMessages.SERVICEUNAVAILABLE.toString());
197         }
198         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
199         if (networkInterface == null) {
200             throw new ServiceUnavailableException("Network CRUD Interface "
201                     + RestMessages.SERVICEUNAVAILABLE.toString());
202         }
203         if (input.isSingleton()) {
204             NeutronSubnet singleton = input.getSingleton();
205
206             /*
207              *  Verify that the subnet doesn't already exist (Issue: is a deeper check necessary?)
208              *  the specified network exists, the subnet has a valid network address,
209              *  and that the gateway IP doesn't overlap with the allocation pools
210              *  *then* add the subnet to the cache
211              */
212             if (subnetInterface.subnetExists(singleton.getID())) {
213                 throw new BadRequestException("subnet UUID already exists");
214             }
215             if (!networkInterface.networkExists(singleton.getNetworkUUID())) {
216                 throw new ResourceNotFoundException("network UUID does not exist.");
217             }
218             if (!singleton.isValidCIDR()) {
219                 throw new BadRequestException("invaild CIDR");
220             }
221             if (!singleton.initDefaults()) {
222                 throw new InternalServerErrorException("subnet object could not be initialized properly");
223             }
224             if (singleton.gatewayIP_Pool_overlap()) {
225                 throw new ResourceConflictException("IP pool overlaps with gateway");
226             }
227             Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
228             if (instances != null) {
229                 for (Object instance : instances) {
230                     INeutronSubnetAware service = (INeutronSubnetAware) instance;
231                     int status = service.canCreateSubnet(singleton);
232                     if (status < 200 || status > 299) {
233                         return Response.status(status).build();
234                     }
235                 }
236             }
237             subnetInterface.addSubnet(singleton);
238             if (instances != null) {
239                 for (Object instance : instances) {
240                     INeutronSubnetAware service = (INeutronSubnetAware) instance;
241                     service.neutronSubnetCreated(singleton);
242                 }
243             }
244         } else {
245             List<NeutronSubnet> bulk = input.getBulk();
246             Iterator<NeutronSubnet> i = bulk.iterator();
247             HashMap<String, NeutronSubnet> testMap = new HashMap<String, NeutronSubnet>();
248             Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
249             while (i.hasNext()) {
250                 NeutronSubnet test = i.next();
251
252                 /*
253                  *  Verify that the subnet doesn't already exist (Issue: is a deeper check necessary?)
254                  *  the specified network exists, the subnet has a valid network address,
255                  *  and that the gateway IP doesn't overlap with the allocation pools,
256                  *  and that the bulk request doesn't already contain a subnet with this id
257                  */
258
259                 if (!test.initDefaults()) {
260                     throw new InternalServerErrorException("subnet object could not be initialized properly");
261                 }
262                 if (subnetInterface.subnetExists(test.getID())) {
263                     throw new BadRequestException("subnet UUID already exists");
264                 }
265                 if (testMap.containsKey(test.getID())) {
266                     throw new BadRequestException("subnet UUID already exists");
267                 }
268                 testMap.put(test.getID(), test);
269                 if (!networkInterface.networkExists(test.getNetworkUUID())) {
270                     throw new ResourceNotFoundException("network UUID does not exist.");
271                 }
272                 if (!test.isValidCIDR()) {
273                     throw new BadRequestException("Invalid CIDR");
274                 }
275                 if (test.gatewayIP_Pool_overlap()) {
276                     throw new ResourceConflictException("IP pool overlaps with gateway");
277                 }
278                 if (instances != null) {
279                     for (Object instance : instances) {
280                         INeutronSubnetAware service = (INeutronSubnetAware) instance;
281                         int status = service.canCreateSubnet(test);
282                         if (status < 200 || status > 299) {
283                             return Response.status(status).build();
284                         }
285                     }
286                 }
287             }
288
289             /*
290              * now, each element of the bulk request can be added to the cache
291              */
292             i = bulk.iterator();
293             while (i.hasNext()) {
294                 NeutronSubnet test = i.next();
295                 subnetInterface.addSubnet(test);
296                 if (instances != null) {
297                     for (Object instance : instances) {
298                         INeutronSubnetAware service = (INeutronSubnetAware) instance;
299                         service.neutronSubnetCreated(test);
300                     }
301                 }
302             }
303         }
304         return Response.status(201).entity(input).build();
305     }
306
307     /**
308      * Updates a Subnet */
309
310     @Path("{subnetUUID}")
311     @PUT
312     @Produces({ MediaType.APPLICATION_JSON })
313     @Consumes({ MediaType.APPLICATION_JSON })
314     //@TypeHint(OpenStackSubnets.class)
315     @StatusCodes({
316             @ResponseCode(code = 200, condition = "Operation successful"),
317             @ResponseCode(code = 400, condition = "Bad Request"),
318             @ResponseCode(code = 401, condition = "Unauthorized"),
319             @ResponseCode(code = 403, condition = "Forbidden"),
320             @ResponseCode(code = 404, condition = "Not Found"),
321             @ResponseCode(code = 501, condition = "Not Implemented") })
322     public Response updateSubnet(
323             @PathParam("subnetUUID") String subnetUUID, final NeutronSubnetRequest input
324             ) {
325         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
326         if (subnetInterface == null) {
327             throw new ServiceUnavailableException("Subnet CRUD Interface "
328                     + RestMessages.SERVICEUNAVAILABLE.toString());
329         }
330
331         /*
332          * verify the subnet exists and there is only one delta provided
333          */
334         if (!subnetInterface.subnetExists(subnetUUID)) {
335             throw new ResourceNotFoundException("subnet UUID does not exist.");
336         }
337         if (!input.isSingleton()) {
338             throw new BadRequestException("Only singleton edit supported");
339         }
340         NeutronSubnet delta = input.getSingleton();
341         NeutronSubnet original = subnetInterface.getSubnet(subnetUUID);
342
343         /*
344          * updates restricted by Neutron
345          */
346         if (delta.getID() != null || delta.getTenantID() != null ||
347                 delta.getIpVersion() != null || delta.getCidr() != null ||
348                 delta.getAllocationPools() != null) {
349             throw new BadRequestException("Attribute edit blocked by Neutron");
350         }
351
352         Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
353         if (instances != null) {
354             for (Object instance : instances) {
355                 INeutronSubnetAware service = (INeutronSubnetAware) instance;
356                 int status = service.canUpdateSubnet(delta, original);
357                 if (status < 200 || status > 299) {
358                     return Response.status(status).build();
359                 }
360             }
361         }
362
363         /*
364          * update the object and return it
365          */
366         subnetInterface.updateSubnet(subnetUUID, delta);
367         NeutronSubnet updatedSubnet = subnetInterface.getSubnet(subnetUUID);
368         if (instances != null) {
369             for (Object instance : instances) {
370                 INeutronSubnetAware service = (INeutronSubnetAware) instance;
371                 service.neutronSubnetUpdated(updatedSubnet);
372             }
373         }
374         return Response.status(200).entity(
375                 new NeutronSubnetRequest(subnetInterface.getSubnet(subnetUUID))).build();
376     }
377
378     /**
379      * Deletes a Subnet */
380
381     @Path("{subnetUUID}")
382     @DELETE
383     @StatusCodes({
384             @ResponseCode(code = 204, condition = "No Content"),
385             @ResponseCode(code = 401, condition = "Unauthorized"),
386             @ResponseCode(code = 404, condition = "Not Found"),
387             @ResponseCode(code = 409, condition = "Conflict"),
388             @ResponseCode(code = 501, condition = "Not Implemented") })
389     public Response deleteSubnet(
390             @PathParam("subnetUUID") String subnetUUID) {
391         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
392         if (subnetInterface == null) {
393             throw new ServiceUnavailableException("Network CRUD Interface "
394                     + RestMessages.SERVICEUNAVAILABLE.toString());
395         }
396
397         /*
398          * verify the subnet exists and it isn't currently in use
399          */
400         if (!subnetInterface.subnetExists(subnetUUID)) {
401             throw new ResourceNotFoundException("subnet UUID does not exist.");
402         }
403         if (subnetInterface.subnetInUse(subnetUUID)) {
404             return Response.status(409).build();
405         }
406         NeutronSubnet singleton = subnetInterface.getSubnet(subnetUUID);
407         Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
408         if (instances != null) {
409             for (Object instance : instances) {
410                 INeutronSubnetAware service = (INeutronSubnetAware) instance;
411                 int status = service.canDeleteSubnet(singleton);
412                 if (status < 200 || status > 299) {
413                     return Response.status(status).build();
414                 }
415             }
416         }
417
418         /*
419          * remove it and return 204 status
420          */
421         subnetInterface.removeSubnet(subnetUUID);
422         if (instances != null) {
423             for (Object instance : instances) {
424                 INeutronSubnetAware service = (INeutronSubnetAware) instance;
425                 service.neutronSubnetDeleted(singleton);
426             }
427         }
428         return Response.status(204).build();
429     }
430 }