Simple Load Balancer Application - Recommitting the source codewith the whole source...
[controller.git] / opendaylight / samples / northbound / loadbalancer / src / main / java / org / opendaylight / controller / samples / loadbalancer / northbound / LoadBalancerNorthbound.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 package org.opendaylight.controller.samples.loadbalancer.northbound;
9
10 import java.util.List;
11
12 import javax.ws.rs.Consumes;
13 import javax.ws.rs.DELETE;
14 import javax.ws.rs.GET;
15 import javax.ws.rs.POST;
16 import javax.ws.rs.PUT;
17 import javax.ws.rs.Path;
18 import javax.ws.rs.PathParam;
19 import javax.ws.rs.Produces;
20 import javax.ws.rs.core.MediaType;
21 import javax.ws.rs.core.Response;
22 import javax.xml.bind.JAXBElement;
23
24 import org.codehaus.enunciate.jaxrs.ResponseCode;
25 import org.codehaus.enunciate.jaxrs.StatusCodes;
26 import org.codehaus.enunciate.jaxrs.TypeHint;
27 import org.opendaylight.controller.containermanager.IContainerManager;
28 import org.opendaylight.controller.samples.loadbalancer.entities.Pool;
29 import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember;
30 import org.opendaylight.controller.samples.loadbalancer.entities.VIP;
31 import org.opendaylight.controller.northbound.commons.RestMessages;
32 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
33 import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException;
34 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
36 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
37 import org.opendaylight.controller.northbound.commons.exception.UnsupportedMediaTypeException;
38 import org.opendaylight.controller.sal.utils.ServiceHelper;
39 import org.opendaylight.controller.samples.loadbalancer.IConfigManager;
40
41 /**
42  * This class exposes North bound REST APIs for the Load Balancer Service.
43  * Following APIs are exposed by the Load Balancer Service:
44  * 
45  * Data retrieval REST APIs::
46  *      1. Get details of all existing pools
47  *              Type : GET  
48  *              URI : /one/nb/v2/lb/{container-name}/
49  *      NOTE: Current implementation of the opendaylight usage 'default' as a container-name
50  *      e.g : http://localhost:8080/one/nb/v2/lb/default will give you list of all the pools
51  *      
52  *      2. Get details of all the existing VIPs
53  *              Type : GET
54  *              URI:  /one/nb/v2/lb/{container-name}/vips
55  * 
56  * Pool related REST APIs::
57  *      1. Create Pool : 
58  *              Type : POST
59  *              URI : /one/nb/v2/lb/{container-name}/create/pool
60  *              Request body :
61  *                      {
62  *                              "name":"",
63  *                              "lbmethod":""
64  *                      }
65  *              Currently, two load balancing policies are allowed {"roundrobin" and "random" }
66  * 
67  *      2. Delete Pool : 
68  *              Type : DELETE
69  *              URI : /one/nb/v2/lb/{container-name}/delete/pool/{pool-name}
70  * 
71  * VIP related REST APIs::
72  *      1. Create VIP: 
73  *              Type : POST
74  *              URI : /one/nb/v2/lb/{container-name}/create/vip
75  *              Request body :
76  *                      {
77  *                              "name":"",
78  *                              "ip":"ip in (xxx.xxx.xxx.xxx) format",
79  *                              "protocol":"TCP/UDP",
80  *                              "port":"any valid port number",
81  *                              "poolname":"" (optional)
82  *                       }
83  *              The pool name is optional and can be set up at a later stage (using the REST API given below).
84  * 
85  *      2. Update VIP: Update pool name of the VIP
86  *              Type : PUT
87  *              URI : /one/nb/v2/lb/{container-name}/update/vip
88  *              Request body :
89  *                      {
90  *                              "name":"",
91  *                              "poolname":""
92  *                       }
93  *              Currently, we only allow update of the VIP pool name (if a VIP does not have an attached pool)
94  *              and not of the VIP name itself.
95  *              The specified pool name must already exist. If the specified VIP is already attached to a pool, the update
96  *              will fail.
97  * 
98  *      3. Delete VIP : 
99  *              Type : DELETE
100  *              URI : /one/nb/v2/lb/{container-name}/delete/vip/{vip-name} 
101  * 
102  * Pool member related REST APIs::
103  *      1. Create pool member:
104  *              Type : POST
105  *              URI : /one/nb/v2/lb/default/create/poolmember
106  *              Request body :
107  *                      {
108  *                              "name":"",
109  *                              "ip":"ip in (xxx.xxx.xxx.xxx) format",
110  *                              "poolname":"existing pool name"
111  *                       }
112  * 
113  *      2. Delete pool member:
114  *              Type : DELETE
115  *              URI     : /one/nb/v2/lb/{container-name}/delete/poolmember/{pool-member-name}/{pool-name}
116  *      
117  *      NOTE: Property "name" of each individual entity must be unique. 
118  *      All the above REST APIs throw appropriate response codes in case of error/success. 
119  *      Please consult the respective methods to get details of various response codes.
120  */
121
122 @Path("/")
123 public class LoadBalancerNorthbound {
124     
125     /*
126      * Method returns the Load balancer service instance running within
127      * 'default' container.
128      */
129     private IConfigManager getConfigManagerService(String containerName) {
130         IContainerManager containerManager = (IContainerManager) ServiceHelper
131                 .getGlobalInstance(IContainerManager.class, this);
132         if (containerManager == null) {
133             throw new ServiceUnavailableException("Container "
134                     + RestMessages.SERVICEUNAVAILABLE.toString());
135         }
136
137         boolean found = false;
138         List<String> containerNames = containerManager.getContainerNames();
139         for (String cName : containerNames) {
140             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
141                 found = true;
142             }
143         }
144
145         if (found == false) {
146             throw new ResourceNotFoundException(containerName + " "
147                     + RestMessages.NOCONTAINER.toString());
148         }
149
150         IConfigManager configManager = (IConfigManager) ServiceHelper.getInstance(
151                         IConfigManager.class, containerName, this);
152
153         if (configManager == null) {
154             throw new ServiceUnavailableException("Load Balancer"
155                     + RestMessages.SERVICEUNAVAILABLE.toString());
156         }
157
158         return configManager;
159     }
160
161     @Path("/{containerName}")
162     @GET
163     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
164     @TypeHint(Pools.class)
165     @StatusCodes( {
166         @ResponseCode(code = 200, condition = "Operation successful"),
167         @ResponseCode(code = 404, condition = "The containerName is not found"),
168         @ResponseCode(code = 503, condition = "Load balancer service is unavailable") })
169     public Pools getAllPools(
170             @PathParam("containerName") String containerName) {
171         
172         IConfigManager configManager = getConfigManagerService(containerName);
173         if (configManager == null) {
174             throw new ServiceUnavailableException("Load Balancer "
175                                                     + RestMessages.SERVICEUNAVAILABLE.toString());
176         }
177         
178         return new Pools(configManager.getAllPools());
179     }
180
181     @Path("/{containerName}/vips")
182     @GET
183     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
184     @TypeHint(VIPs.class)
185     @StatusCodes( {
186         @ResponseCode(code = 200, condition = "Operation successful"),
187         @ResponseCode(code = 404, condition = "The containerName is not found"),
188         @ResponseCode(code = 503, condition = "Load balancer service is unavailable") })
189     public VIPs getAllVIPs(
190             @PathParam("containerName") String containerName) {
191         
192         IConfigManager configManager = getConfigManagerService(containerName);
193         if (configManager == null) {
194             throw new ServiceUnavailableException("Load Balancer "
195                     + RestMessages.SERVICEUNAVAILABLE.toString());
196         }
197
198         return new VIPs(configManager.getAllVIPs());
199     }
200
201     @Path("/{containerName}/create/vip")
202     @POST
203     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
204     @StatusCodes( {
205         @ResponseCode(code = 201, condition = "VIP created successfully"),
206         @ResponseCode(code = 404, condition = "The Container Name not found"),
207         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
208         @ResponseCode(code = 409, condition = "VIP already exist"),
209         @ResponseCode(code = 415, condition = "Invalid input data")})
210     public Response addVIP(@PathParam("containerName") String containerName,
211             @TypeHint(VIP.class) JAXBElement<VIP> inVIP){
212         
213         VIP vipInput = inVIP.getValue();
214         String name = vipInput.getName();
215         String ip = vipInput.getIp();
216         String protocol = vipInput.getProtocol();
217         short protocolPort = vipInput.getPort();
218         String poolName = vipInput.getPoolName();
219         if(name.isEmpty() ||
220                 ip.isEmpty()||
221                 protocol.isEmpty()||
222                 protocolPort < 0 ){
223             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
224         }
225         
226         IConfigManager configManager = getConfigManagerService(containerName);
227         
228         if (configManager == null) {
229             throw new ServiceUnavailableException("Load Balancer "
230                     + RestMessages.SERVICEUNAVAILABLE.toString());
231         }
232         
233         if(!configManager.vipExists(name, ip, protocol, protocolPort, poolName)){
234             
235             VIP vip = configManager.createVIP(name, ip, protocol, protocolPort, poolName);
236             if ( vip != null){
237                 return Response.status(Response.Status.CREATED).build();
238             }
239         }else{
240             throw new ResourceConflictException(NBConst.RES_VIP_ALREADY_EXIST);
241         }
242         throw new InternalServerErrorException(NBConst.RES_VIP_CREATION_FAILED);
243     }
244
245     @Path("/{containerName}/update/vip")
246     @PUT
247     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
248     @StatusCodes( {
249         @ResponseCode(code = 201, condition = "VIP updated successfully"),
250         @ResponseCode(code = 404, condition = "The containerName not found"),
251         @ResponseCode(code = 503, condition = "VIP not found"),
252         @ResponseCode(code = 404, condition = "Pool not found"),
253         @ResponseCode(code = 405, condition = "Pool already attached to the VIP"),
254         @ResponseCode(code = 415, condition = "Invalid input name")})
255     public Response updateVIP(@PathParam("containerName") String containerName,
256             @TypeHint(VIP.class) JAXBElement<VIP> inVIP) {
257         
258         VIP vipInput = inVIP.getValue();
259         String name = vipInput.getName();
260         String poolName = vipInput.getPoolName();
261         if(name.isEmpty() ||
262                 poolName.isEmpty()){
263             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
264         }
265         
266         IConfigManager configManager = getConfigManagerService(containerName);
267         if (configManager == null) {
268             throw new ServiceUnavailableException("Load Balancer "
269                                                 + RestMessages.SERVICEUNAVAILABLE.toString());
270         }
271         
272         if(!configManager.poolExists(poolName))
273             throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND);
274         
275         if(configManager.getVIPAttachedPool(name)!=null)
276             throw new MethodNotAllowedException(NBConst.RES_VIP_POOL_EXIST);
277         
278         if(configManager.updateVIP(name, poolName)!= null)
279             return Response.status(Response.Status.ACCEPTED).build();
280         
281         throw new InternalServerErrorException(NBConst.RES_VIP_UPDATE_FAILED);
282     }
283     
284     @Path("/{containerName}/delete/vip/{vipName}")
285     @DELETE
286     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
287     @StatusCodes( {
288         @ResponseCode(code = 200, condition = "VIP deleted successfully"),
289         @ResponseCode(code = 404, condition = "The containerName not found"),
290         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
291         @ResponseCode(code = 404, condition = "VIP not found"),
292         @ResponseCode(code = 500, condition = "Failed to delete VIP")})
293     public Response deleteVIP(
294             @PathParam(value = "containerName") String containerName,
295             @PathParam(value = "vipName") String vipName) {
296         
297         if(vipName.isEmpty())
298             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
299         
300         IConfigManager configManager = getConfigManagerService(containerName);
301         if (configManager == null) {
302             throw new ServiceUnavailableException("Load Balancer"
303                                             + RestMessages.SERVICEUNAVAILABLE.toString());
304         }
305         
306         if(!configManager.vipExists(vipName))
307             throw new ResourceNotFoundException(NBConst.RES_VIP_NOT_FOUND);
308         
309         for(VIP vip : configManager.getAllVIPs()){
310             if(vip.getName().equals(vipName)){
311                 configManager.deleteVIP(vipName);
312                 return Response.ok().build();
313             }
314         }
315         throw new InternalServerErrorException(NBConst.RES_VIP_DELETION_FAILED);
316     }
317
318     @Path("/{containerName}/create/pool")
319     @POST
320     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
321     @StatusCodes( {
322         @ResponseCode(code = 201, condition = "Pool created successfully"),
323         @ResponseCode(code = 404, condition = "The containerName not found"),
324         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
325         @ResponseCode(code = 409, condition = "Pool already exist"),
326         @ResponseCode(code = 415, condition = "Invalid input data")})
327     public Response addPool(@PathParam("containerName") String containerName,
328             @TypeHint(Pool.class) JAXBElement<Pool> inPool) {
329         
330         Pool poolInput = inPool.getValue();
331         String name = poolInput.getName();
332         String lbMethod =poolInput.getLbMethod();
333         if(name.isEmpty() ||
334                 lbMethod.isEmpty()){
335             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
336         }
337         
338         IConfigManager configManager = getConfigManagerService(containerName);
339         if (configManager == null) {
340             throw new ServiceUnavailableException("Load Balancer "
341                                             + RestMessages.SERVICEUNAVAILABLE.toString());
342         }
343         
344         if(!configManager.poolExists(name)){
345             
346             Pool pool = configManager.createPool(name, lbMethod);
347             if ( pool != null){
348                 return Response.status(Response.Status.CREATED).build();
349             }
350         }else{
351             throw new ResourceConflictException(NBConst.RES_POOL_ALREADY_EXIST);
352         }
353         throw new InternalServerErrorException(NBConst.RES_POOL_CREATION_FAILED);
354     }
355
356     @Path("/{containerName}/delete/pool/{poolName}")
357     @DELETE
358     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
359     @StatusCodes( {
360         @ResponseCode(code = 200, condition = "Pool deleted successfully"),
361         @ResponseCode(code = 404, condition = "The containerName not found"),
362         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
363         @ResponseCode(code = 404, condition = "Pool not found"),
364         @ResponseCode(code = 500, condition = "Failed to delete Pool")})
365     public Response deletePool(
366             @PathParam(value = "containerName") String containerName,
367             @PathParam(value = "poolName") String poolName) {
368         
369         if(poolName.isEmpty())
370             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
371         
372         IConfigManager configManager = getConfigManagerService(containerName);
373         if (configManager == null) {
374             throw new ServiceUnavailableException("Load Balancer"
375                                         + RestMessages.SERVICEUNAVAILABLE.toString());
376         }
377         
378         if(!configManager.poolExists(poolName))
379             throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND);
380         
381         for(Pool pool:configManager.getAllPools()){
382             if(pool.getName().equals(poolName)){
383                 configManager.deletePool(poolName);
384                 return Response.ok().build();
385             }
386         }
387         throw new InternalServerErrorException(NBConst.RES_POOL_DELETION_FAILED);
388     }
389
390     @Path("/{containerName}/create/poolmember")
391     @POST
392     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
393     @StatusCodes( {
394         @ResponseCode(code = 201, condition = "Pool member created successfully"),
395         @ResponseCode(code = 404, condition = "The containerName not found"),
396         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
397         @ResponseCode(code = 404, condition = "Pool not found"),
398         @ResponseCode(code = 409, condition = "Pool member already exist"),
399         @ResponseCode(code = 415, condition = "Invalid input data")})
400     public Response addPoolMember(@PathParam("containerName") String containerName,
401             @TypeHint(PoolMember.class) JAXBElement<PoolMember> inPoolMember){
402         
403         PoolMember pmInput = inPoolMember.getValue();
404         String name = pmInput.getName();
405         String memberIP = pmInput.getIp();
406         String poolName = pmInput.getPoolName();
407         
408         if(name.isEmpty() ||
409                 memberIP.isEmpty()||
410                 poolName.isEmpty()){
411             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
412         }
413         
414         IConfigManager configManager = getConfigManagerService(containerName);
415         if (configManager == null) {
416             throw new ServiceUnavailableException("Load Balancer "
417                                         + RestMessages.SERVICEUNAVAILABLE.toString());
418         }
419         
420         if(!configManager.poolExists(poolName))
421             throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND);
422         
423         if(!configManager.memberExists(name, memberIP, poolName)){
424             
425             PoolMember poolMember = configManager.addPoolMember(name, memberIP, poolName);
426             if ( poolMember != null){
427                 return Response.status(Response.Status.CREATED).build();
428             }
429         }else{
430             throw new ResourceConflictException(NBConst.RES_POOLMEMBER_ALREADY_EXIST);
431         }
432         throw new InternalServerErrorException(NBConst.RES_POOLMEMBER_CREATION_FAILED);
433     }
434
435     @Path("/{containerName}/delete/poolmember/{poolMemberName}/{poolName}")
436     @DELETE
437     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
438     @StatusCodes( {
439         @ResponseCode(code = 200, condition = "Pool member deleted successfully"),
440         @ResponseCode(code = 404, condition = "The containerName not found"),
441         @ResponseCode(code = 503, condition = "Load balancer service is unavailable"),
442         @ResponseCode(code = 404, condition = "Pool member not found"),
443         @ResponseCode(code = 404, condition = "Pool not found")})
444     public Response deletePoolMember(
445             @PathParam(value = "containerName") String containerName,
446             @PathParam(value = "poolMemberName") String poolMemberName,
447             @PathParam(value = "poolName") String poolName) {
448         
449         if(poolMemberName.isEmpty()||
450                 poolName.isEmpty())
451             throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString());
452         
453         IConfigManager configManager = getConfigManagerService(containerName);
454         
455         if (configManager == null) {
456             throw new ServiceUnavailableException("Load Balancer"
457                                         + RestMessages.SERVICEUNAVAILABLE.toString());
458         }
459         
460         if(!configManager.poolExists(poolName))
461             throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND);
462         
463         if(configManager.memberExists(poolMemberName, poolName)){
464             
465             configManager.removePoolMember(poolMemberName, poolName);
466             
467             return Response.ok().build();
468         }
469         throw new ResourceNotFoundException(NBConst.RES_POOLMEMBER_NOT_FOUND);
470     }
471 }