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