From bf9ead90c2584be7c6bafff0a49aec3db62f54da Mon Sep 17 00:00:00 2001 From: Anilkumar Vishnoi Date: Thu, 11 Apr 2013 03:47:12 +0530 Subject: [PATCH] Simple Load Balancer Application - Recommitting the source codewith the whole source code of the application in single commit Signed-off-by: Anilkumar Vishnoi --- .../distribution/opendaylight/pom.xml | 2 + opendaylight/samples/loadbalancer/pom.xml | 97 ++++ .../samples/loadbalancer/ConfigManager.java | 292 ++++++++++ .../samples/loadbalancer/IConfigManager.java | 182 ++++++ .../samples/loadbalancer/LBConst.java | 33 ++ .../samples/loadbalancer/LBUtil.java | 102 ++++ .../samples/loadbalancer/entities/Client.java | 142 +++++ .../samples/loadbalancer/entities/Pool.java | 276 +++++++++ .../loadbalancer/entities/PoolMember.java | 178 ++++++ .../samples/loadbalancer/entities/VIP.java | 200 +++++++ .../loadbalancer/internal/Activator.java | 138 +++++ .../internal/LoadBalancerService.java | 533 ++++++++++++++++++ .../policies/ILoadBalancingPolicy.java | 29 + .../loadbalancer/policies/RandomLBPolicy.java | 108 ++++ .../policies/RoundRobinLBPolicy.java | 153 +++++ .../internal/LoadBalancerTest.java | 69 +++ .../northbound/loadbalancer/enunciate.xml | 12 + .../samples/northbound/loadbalancer/pom.xml | 108 ++++ .../northbound/LoadBalancerNorthbound.java | 471 ++++++++++++++++ .../LoadBalancerNorthboundRSApplication.java | 28 + .../loadbalancer/northbound/NBConst.java | 43 ++ .../loadbalancer/northbound/Pools.java | 51 ++ .../samples/loadbalancer/northbound/VIPs.java | 53 ++ .../main/resources/META-INF/spring.factories | 1 + .../main/resources/META-INF/spring.handlers | 10 + .../main/resources/META-INF/spring.schemas | 49 ++ .../main/resources/META-INF/spring.tooling | 39 ++ .../main/resources/WEB-INF/spring/context.xml | 10 + .../WEB-INF/spring/servlet/security.xml | 36 ++ .../src/main/resources/WEB-INF/web.xml | 43 ++ 30 files changed, 3488 insertions(+) create mode 100644 opendaylight/samples/loadbalancer/pom.xml create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/ConfigManager.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/IConfigManager.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBConst.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBUtil.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Client.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Pool.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/PoolMember.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/VIP.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/Activator.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerService.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/ILoadBalancingPolicy.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RandomLBPolicy.java create mode 100644 opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RoundRobinLBPolicy.java create mode 100644 opendaylight/samples/loadbalancer/src/test/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerTest.java create mode 100644 opendaylight/samples/northbound/loadbalancer/enunciate.xml create mode 100644 opendaylight/samples/northbound/loadbalancer/pom.xml create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthbound.java create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/NBConst.java create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/Pools.java create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/VIPs.java create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.factories create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.handlers create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.schemas create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.tooling create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/context.xml create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/servlet/security.xml create mode 100644 opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index f6f9a01830..f6a7808b23 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -86,6 +86,8 @@ ../../samples/simpleforwarding + ../../samples/loadbalancer + ../../samples/northbound/loadbalancer diff --git a/opendaylight/samples/loadbalancer/pom.xml b/opendaylight/samples/loadbalancer/pom.xml new file mode 100644 index 0000000000..aaf1a183ca --- /dev/null +++ b/opendaylight/samples/loadbalancer/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../commons/opendaylight + + org.opendaylight.controller + samples.loadbalancer + 0.4.0-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + org.opendaylight.controller.samples.loadbalancer, + org.opendaylight.controller.samples.loadbalancer.entities, + org.opendaylight.controller.samples.loadbalancer.internal, + org.opendaylight.controller.samples.loadbalancer.policies + + + org.opendaylight.controller.sal.core, + org.opendaylight.controller.sal.utils, + org.opendaylight.controller.sal.action, + org.opendaylight.controller.sal.match, + org.opendaylight.controller.sal.packet, + org.opendaylight.controller.sal.routing, + org.opendaylight.controller.sal.flowprogrammer, + org.opendaylight.controller.sal.packet.address, + org.opendaylight.controller.hosttracker, + org.opendaylight.controller.hosttracker.hostAware, + org.opendaylight.controller.samples.loadbalancer, + org.opendaylight.controller.samples.loadbalancer.entities, + org.opendaylight.controller.samples.loadbalancer.internal, + org.opendaylight.controller.samples.loadbalancer.policies, + org.opendaylight.controller.topologymanager, + org.opendaylight.controller.forwardingrulesmanager, + org.opendaylight.controller.switchmanager, + org.opendaylight.controller.clustering.services, + javax.xml.bind.annotation, + javax.xml.bind, + org.apache.felix.dm, + org.apache.commons.lang3.builder, + org.osgi.service.component, + org.slf4j, + org.eclipse.osgi.framework.console, + org.osgi.framework + + + org.opendaylight.controller.samples.loadbalancer.internal.Activator + + + + + + + + + + + org.opendaylight.controller + topologymanager + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + switchmanager + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + forwardingrulesmanager + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + hosttracker + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + sal + 0.4.0-SNAPSHOT + + + diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/ConfigManager.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/ConfigManager.java new file mode 100644 index 0000000000..3cacab7846 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/ConfigManager.java @@ -0,0 +1,292 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents a configuration manager for the Load Balancer service. + * This class is responsible for managing(store/update/delete) the load balancer + * configuration that it receives through REST APIs or from any other applications + * present in the controller. + * + */ +public class ConfigManager implements IConfigManager{ + + /* + * Logger instance + */ + private static final Logger cmLogger = LoggerFactory.getLogger(ConfigManager.class); + + /* + * All the available VIPs + */ + private HashMap vips = new HashMap(); + + /* + * All the available Pools + */ + private HashMap pools = new HashMap(); + + public ConfigManager(){ + } + + @Override + public boolean vipExists(String name) { + return this.vips.containsKey(name); + } + + @Override + public boolean vipExists(VIP vip){ + if(vip.getName()==null){ + if(!vips.containsValue(vip)){ + return false; + } + }else{ + if(!vips.containsKey(vip.getName())){ + if(!vips.containsValue(vip)){ + return false; + } + } + } + return true; + } + + @Override + public boolean vipExists(String name,String ip,String protocol,short protocolPort,String poolName){ + + VIP vip = new VIP(name,ip,protocol,protocolPort,poolName); + + //Check VIP with the same name + + if(!vips.containsKey(name)){ + //Check VIP with the same ip, protocol and protocolPort + if(!vips.containsValue(vip)){ + + //if you reach here, means this VIP don't exist + return false; + } + } + + //Yeah, i have it. + return true; + } + + @Override + public Set getAllVIPs(){ + return new HashSet(this.vips.values()); + } + + public VIP getVIPWithPoolName(VIP vip){ + cmLogger.info("Search a VIP with name:{}",vip); + for(VIP vipTemp: this.vips.values()){ + if(vipTemp.equals(vip)){ + + cmLogger.info("Found VIP with pool detail : {}",vipTemp); + return vipTemp; + } + } + + cmLogger.info("VIP with pool detail not found "); + return null; + } + + @Override + public VIP createVIP(String name,String ip,String protocol,short protocolPort,String poolName){ + + cmLogger.info("Create VIP with the following details :[ name : "+name + +" ip : "+ip + +" protocol : "+protocol + +" protocol_port : "+protocolPort + +" pool name : "+poolName); + + VIP vip = new VIP(name,ip,protocol,protocolPort,poolName); + + if(poolName !=null && !poolName.isEmpty()){ + if(pools.containsKey(poolName)){ + pools.get(poolName).addVIP(vip); + } + } + + vip.setStatus(LBConst.STATUS_ACTIVE); + this.vips.put(name, vip); + + cmLogger.info("New VIP created : "+vip.toString()); + return vip; + } + + @Override + public String getVIPAttachedPool(String name) { + return this.vips.get(name).getPoolName(); + } + + @Override + public VIP updateVIP(String name, String poolName){ + + cmLogger.info("Updating VIP : "+name+" pool name to "+poolName); + + if(vips.containsKey(name)){ + VIP vip = vips.get(name); + if(vip.getPoolName() == null){ + vip.setPoolName(poolName); + cmLogger.error("VIP is now attached to the pool : {}",vip.toString()); + return vip; + } + cmLogger.error("VIP is already attached to one pool : {}",vip.toString()); + } + cmLogger.error("VIP with name: "+name+" does not exist"); + return null; + } + + @Override + public VIP deleteVIP(String name){ + + cmLogger.info("Deleting VIP : "+name); + + VIP vip = vips.get(name); + + String poolName = vip.getPoolName(); + + if(poolName != null){ + if(pools.containsKey(poolName)){ + Pool pool = pools.get(poolName); + pool.removeVIP(vip.getName()); + } + } + + cmLogger.info("VIP removed : "+vip.toString()); + + vips.remove(vip.getName()); + + return vip; + } + + @Override + public boolean memberExists(String name, String poolName) { + if(this.pools.containsKey(poolName)){ + if(this.pools.get(poolName).getMember(name) != null ) + return true; + } + return false; + } + + @Override + public boolean memberExists(String name, String memberIP,String poolName){ + if(!this.pools.containsKey(poolName)) + return false; + + return this.pools.get(poolName).poolMemberExists(new PoolMember(name, memberIP, poolName)); + } + + @Override + public PoolMember addPoolMember(String name, String memberIP, String poolName){ + + PoolMember pm = new PoolMember(name,memberIP,poolName); + + cmLogger.info("Adding pool member : "+pm.toString()); + + pools.get(poolName).addMember(pm); + + return pm; + } + + @Override + public PoolMember removePoolMember(String name, String poolName){ + + cmLogger.info("Removing pool member : {} from pool {}",name, poolName); + + Pool pool = pools.get(poolName); + + PoolMember pm = pool.getMember(name); + + pool.removeMember(name); + + cmLogger.info("Pool member {} removed from {} ",name,poolName); + + return pm; + } + + @Override + public Set getAllPools(){ + return new HashSet(this.pools.values()); + } + + @Override + public boolean poolExists(String name) { + return this.pools.containsKey(name); + } + + @Override + public boolean poolExists(String name, String lbMethod){ + + return pools.containsValue(new Pool(name,lbMethod)); + } + + @Override + public Pool createPool(String name, String lbMethod){ + + Pool newPool = new Pool(name,lbMethod); + + cmLogger.info("New pool created : " + newPool.toString()); + + pools.put(name, newPool); + + return newPool; + } + + @Override + public Pool deletePool(String poolName){ + + Pool pool = pools.get(poolName); + + for(VIP vip:pool.getAllVip()){ + + vip.setPoolName(null); + + } + + cmLogger.info("Pool removed : "+pool.toString()); + + pools.remove(poolName); + + return pool; + } + + @Override + public Pool getPool( String poolName){ + if(pools.containsKey(poolName)){ + return pools.get(poolName); + } + return null; + } + + @Override + public Set getAllPoolMembers(String poolName) { + + if(pools.containsKey(poolName)){ + return new HashSet(pools.get(poolName).getAllMembers()); + } + return null; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ConfigManager [vips=" + vips + ", pools=" + pools + "]"; + } +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/IConfigManager.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/IConfigManager.java new file mode 100644 index 0000000000..df35b0ae1f --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/IConfigManager.java @@ -0,0 +1,182 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer; + +import java.util.Set; + +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; + +/** + * Interface implemented by the configuration manager. + * + */ +public interface IConfigManager { + + /** + * Return all existing VIPs + * @return Set of VIP's + * if there is no VIP, it will return empty set. + */ + public Set getAllVIPs(); + + /** + * Check if VIP with the 'name' exists + * @param name Name of the VIP + * @return true if exists + * false else + */ + public boolean vipExists(String name); + + /** + * Check if VIP exists with the details 'VIP' + * @param vip Search for this VIP + * @return true if exists + * false else + */ + public boolean vipExists(VIP vip); + + /** + * Check if VIP with the provided details exists + * @param name Name of the VIP + * @param ip IP of the VIP + * @param protocol IP Protocol of the VIP (TCP/UDP) + * @param protocolPort Transport port of the VIP (e.g 5550) + * @param poolName Name of the pool attached with the VIP + * @return true if exists + * false else + */ + public boolean vipExists(String name,String ip,String protocol,short protocolPort,String poolName); + + /** + * Add VIP to the configuration + * @param name Name of the VIP + * @param ip IP of the VIP + * @param protocol IP Protocol of the VIP (TCP/UDP) + * @param protocolPort Transport port of the VIP + * @param poolName Name of the pool that VIP will use for load balancing its traffic + * @return Newly created VIP + */ + public VIP createVIP(String name,String ip,String protocol,short protocolPort,String poolName); + + /** + * Return pool attached to VIP + * @param name Name of the VIP + * @return Name of the pool attached to VIP + * else null + */ + public String getVIPAttachedPool(String name); + /** + * Update pool name of the VIP. + * @param name Name of the VIP + * @param poolName Attach this pool to VIP + * @return Updated VIP If successful + * null If this VIP is already attached to any existing pool. + */ + public VIP updateVIP(String name, String poolName); + + /** + * Delete the VIP + * @param name Delete VIP with this name + * @return Details of the deleted VIP + */ + public VIP deleteVIP(String name); + + /** + * Check if pool member with the 'name' present in the pool with name 'poolName' + * @param name Name of the pool member + * @param poolName Name of the pool, you want to search for pool member + * @return true If exist + * false else + */ + public boolean memberExists(String name, String poolName); + + /** + * Check if pool member with name 'name' and IP 'memberIP' exist in the pool 'poolName' + * @param name Name of the pool member + * @param memberIP IP of the pool member + * @param poolName Name of the pool member you want to search + * @return true If Exist + * false else + */ + public boolean memberExists(String name, String memberIP,String poolName); + + /** + * Return all pool members of the pool 'poolName' + * @param poolName Name of the pool + * @return Set of all the pool members if pool with the name present in the configuration + * null else + * + */ + public Set getAllPoolMembers(String poolName); + + /** + * Add new pool member to the configuration + * @param name Name of the pool + * @param memberIP IP of the pool + * @param poolName Attach pool member to this pool + * @return Newly created pool member + */ + public PoolMember addPoolMember(String name, String memberIP, String poolName); + + /** + * Remove pool member from the pool + * @param name Name of the pool member + * @param poolName Name of the pool + * @return Details of the removed pool member + */ + public PoolMember removePoolMember(String name, String poolName); + + /** + * Return all the existing pools + * @return Set of Pools + */ + public Set getAllPools(); + + /** + * Return pool with input name + * @param poolName Name of the pool + * @return Details of the pool if pool exist + * null else + */ + public Pool getPool(String poolName); + + /** + * Check if pool exists with the input name + * @param name Name of the pool + * @return true If exists + * false else + */ + public boolean poolExists(String name); + + /** + * Check if pool exists with the input name and loadbalancing method. + * @param name Name of the pool + * @param lbMethod Load balancing method name + * @return true If exists + * false else + */ + public boolean poolExists(String name, String lbMethod); + + /** + * Create new pool with the provided details + * @param name Name of the pool + * @param lbMethod Load balancing method this pool will use + * @return Details of the newly created pool + */ + public Pool createPool(String name, String lbMethod); + + /** + * Delete pool with the provided name + * @param poolName Name of the pool + * @return Details of the deleted pool + */ + public Pool deletePool(String poolName); + +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBConst.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBConst.java new file mode 100644 index 0000000000..7e93304de1 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBConst.java @@ -0,0 +1,33 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer; + +/** + * Class defines all the constants used by load balancer service + * + */ +public class LBConst { + + public static final int FORWARD_DIRECTION_LB_FLOW = 0; + + public static final int REVERSE_DIRECTION_LB_FLOW = 1; + + public static final String ROUND_ROBIN_LB_METHOD = "roundrobin"; + + public static final String RANDOM_LB_METHOD = "random"; + + public static final String STATUS_ACTIVE="active"; + + public static final String STATUS_INACTIVE="inactive"; + + public static final String STATUS_PENDING="pending"; + + public static final String STATUS_ERROR="error"; + +} + diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBUtil.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBUtil.java new file mode 100644 index 0000000000..c320250084 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/LBUtil.java @@ -0,0 +1,102 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer; + +import org.opendaylight.controller.sal.packet.IPv4; +import org.opendaylight.controller.sal.packet.Packet; +import org.opendaylight.controller.sal.packet.TCP; +import org.opendaylight.controller.sal.packet.UDP; +import org.opendaylight.controller.sal.utils.IPProtocols; +import org.opendaylight.controller.sal.utils.NetUtils; +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class defines utilty methods that will be used by different components + * of the load balancer service + * + */ +public class LBUtil { + + private static final Logger lbuLogger = LoggerFactory.getLogger(LBUtil.class); + + public LBUtil(){} + + /** + * Extract the details of the source machine that sent this packet 'inPkt' + * @param inPkt Packet that is received by the controller + * @return Details of the source machine in Client object. + */ + public Client getClientFromPacket(IPv4 inPkt){ + lbuLogger.info("Find client information from packet : {}",inPkt.toString()); + + String ip = NetUtils.getInetAddress(inPkt.getSourceAddress()).getHostAddress(); + + String protocol = IPProtocols.getProtocolName(inPkt.getProtocol()); + + lbuLogger.info("client ip {} and protocl {}",ip,protocol); + + Packet tpFrame= inPkt.getPayload(); + + lbuLogger.info("Get protocol layer {}",tpFrame.toString()); + + short port = 0; + + if(protocol.equals(IPProtocols.TCP.toString())){ + TCP tcpFrame = (TCP)tpFrame; + port = tcpFrame.getSourcePort(); + }else{ + UDP udpFrame = (UDP)tpFrame; + port = udpFrame.getSourcePort(); + } + + lbuLogger.info("Found port {}",port); + + Client source = new Client(ip, protocol,port); + + lbuLogger.info("Client information : {}",source.toString()); + + return source; + } + + /** + * Extract the details of the destination machine where this packet 'inPkt' need + * to be delivered + * @param inPkt Packet that is received by controller for forwarding + * @return Details of the destination machine packet in VIP + */ + public VIP getVIPFromPacket(IPv4 inPkt){ + + lbuLogger.info("Find VIP information from packet : {}",inPkt.toString()); + + String ip = NetUtils.getInetAddress(inPkt.getDestinationAddress()).getHostAddress(); + + String protocol = IPProtocols.getProtocolName(inPkt.getProtocol()); + + Packet tpFrame= inPkt.getPayload(); + + short port = 0; + + if(protocol.equals(IPProtocols.TCP.toString())){ + TCP tcpFrame = (TCP)tpFrame; + port = tcpFrame.getDestinationPort(); + }else{ + + UDP udpFrame = (UDP)tpFrame; + port = udpFrame.getDestinationPort(); + } + + VIP dest = new VIP(null,ip, protocol,port,null); + + lbuLogger.info("VIP information : {}",dest.toString()); + + return dest; + } +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Client.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Client.java new file mode 100644 index 0000000000..4d7cf4928a --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Client.java @@ -0,0 +1,142 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.entities; + +/** + * This class represents the source host that sends any traffic to any existing virtual IP (VIP). + * This source host is referred to as a 'Client'. Clients will be differentiated from each other + * based on the following three properties: + * 1. IP address of the client. + * 2. Protocol of the traffic it is using for sending traffic + * 3. Source port from which it is sending traffic. + * e.g TCP traffic from two different ports from the same host to a given VIP will be considered + * as two different clients by this service. Similarly, traffic using two different protocols + * (TCP, UDP) from the same host will be considered as two different clients. + * + */ +public class Client { + + /* + * IP address of the client (source address) + */ + private String ip; + + /* + * Network protocol of the traffic sent by client + */ + private String protocol; + + /* + * Port used to send network traffic (source port) + */ + private short port; + + public Client(String ip, String protocol, short port){ + this.ip = ip; + this.protocol = protocol; + this.port = port; + } + + /** + * @return the client IP + */ + public String getIp() { + return ip; + } + + /** + * @param ip the IP to set + */ + public void setIp(String ip) { + this.ip = ip; + } + + /** + * @return the client network protocol + */ + public String getProtocol() { + return protocol; + } + + /** + * @param protocol the protocol to set + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * @return the client port + */ + public short getPort() { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(short port) { + this.port = port; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((ip == null) ? 0 : ip.hashCode()); + result = prime * result + port; + result = prime * result+ ((protocol == null) ? 0 : protocol.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Client)) { + return false; + } + Client other = (Client) obj; + if (ip == null) { + if (other.ip != null) { + return false; + } + }else if (!ip.equals(other.ip)) { + return false; + } + if (port != other.port) { + return false; + } + if (protocol == null) { + if (other.protocol != null) { + return false; + } + }else if (!protocol.equals(other.protocol)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Client [ip=" + ip + ", protocol=" + protocol + ", port=" + port+ "]"; + } +} diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Pool.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Pool.java new file mode 100644 index 0000000000..bbf31162aa --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/Pool.java @@ -0,0 +1,276 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.entities; + +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * This class represents the pool of hosts among which incoming traffic + * will be load balanced. Each pool will load balance the traffic among its pool members + * based on the loadbalancing policy set for the pool. + * Currently, the pool supports two load balancing policies: + * 1. Round Robin Policy{@link org.opendaylight.controller.samples.loadbalancer.policies.RoundRobinLBPolicy} + * 2. Random Policy {@link org.opendaylight.controller.samples.loadbalancer.policies.RandomLBPolicy} + * + * NOTE: After creation of the pool, user can't update (change) its load balancing policy. + * NOTE: Each Pool should have a unique name. + */ + +@XmlRootElement(name="pool") +@XmlAccessorType(XmlAccessType.NONE) +public class Pool { + + /* + * Unique name of the pool + */ + @XmlElement + private String name; + + /* + * Associated load balancing policy + */ + @XmlElement(name="lbmethod") + private String lbMethod; + + /* + * Status of the pool (active/inactive) + */ + @XmlElement + private String status; + + /* + * List of all the VIPs using this pool for load balancing their traffic - more than + * one VIP can be mapped to each pool. + */ + @XmlElement + private ArrayList vips = new ArrayList(); + + /* + * List of all the pool members used for load balancing the traffic + */ + @XmlElement + private ArrayList members = new ArrayList(); + + /* + * Private constructor used for JAXB mapping + */ + @SuppressWarnings("unused") + private Pool() {} + + /** + * Getter/ Setter methods + */ + + public Pool(String name, + String lbMethod) { + this.name = name; + this.lbMethod = lbMethod; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the lbMethod + */ + public String getLbMethod() { + return lbMethod; + } + + /** + * @param lbMethod the lbMethod to set + */ + public void setLbMethod(String lbMethod) { + this.lbMethod = lbMethod; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * @return the vip + */ + public ArrayList getAllVip() { + return vips; + } + + /** + * @param vip the vip to set + */ + public void setVips(ArrayList vips) { + this.vips = vips; + } + + /** + * @return the members + */ + public ArrayList getAllMembers() { + return members; + } + + /** + * @param members the members to set + */ + public void setMembers(ArrayList members) { + this.members = members; + } + + /** + * Add new VIP to the VIP list + * @param vip new VIP to add + */ + public void addVIP(VIP vip){ + this.vips.add(vip); + } + + /** + * Remove VIP with given name from the VIP list of the pool + * @param name Name of the VIP + * @return true If VIP was using this pool and removed + * false IF VIP is not using this pool + */ + public boolean removeVIP(String name){ + for(VIP vip: this.vips){ + if(vip.getName().equals(name)){ + this.members.remove(vip); + return true; + } + } + return false; + } + + /** + * Check if the given pool member is part of this pool + * @param pm Search for this pool member + * @return true If pool member is attached to this pool + * false else + */ + public boolean poolMemberExists(PoolMember pm){ + return this.members.contains(pm); + } + + /** + * Returns the pool member with the given name + * @param name Search for this pool member + * @return PoolMember If pool member is attached to this pool + * null else + */ + public PoolMember getMember(String name){ + + for(PoolMember pm: this.members){ + if(pm.getName().equals(name)){ + return pm; + } + } + return null; + } + + /** + * Add new pool member to the pool + * @param pm Add this new pool + */ + public void addMember(PoolMember pm){ + this.members.add(pm); + } + + /** + * Remove pool member from the pool list + * @param name Remove this pool member + * @return true If pool member was attached to this pool and successfully removed + * false If pool member is not attached to this pool + */ + public boolean removeMember(String name){ + for(PoolMember pm: this.members){ + if(pm.getName().equals(name)){ + this.members.remove(pm); + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result+ ((lbMethod == null) ? 0 : lbMethod.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Pool)) { + return false; + } + Pool other = (Pool) obj; + if (lbMethod == null) { + if (other.lbMethod != null) { + return false; + } + }else if (!lbMethod.equals(other.lbMethod)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + }else if (!name.equals(other.name)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Pool [name=" + name + ", lbMethod=" + lbMethod + ", status=" + + status + ", vips=" + vips + ", members=" + members + "]"; + } +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/PoolMember.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/PoolMember.java new file mode 100644 index 0000000000..31297ebcce --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/PoolMember.java @@ -0,0 +1,178 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.entities; + + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * This class represents the host where load balancing service will + * redirect VIP traffic for load balancing. All these hosts have to + * register with a pool to be a part of traffic load balancing. + * This entity is referred to as a 'PoolMember'. + * Load balancer service differentiates each pool member based on its + * two properties { ip address, attached pool }. + * A host (IP) can be attached to two different pools through creation of two + * different pool member objects. + * + * NOTE: Each pool member should have a unique name. + * + */ +@XmlRootElement(name="poolmember") +@XmlAccessorType(XmlAccessType.NONE) +public class PoolMember { + + /* + * Unique name of the pool member + */ + @XmlElement + private String name; + + /* + * IP address of the pool member + */ + @XmlElement + private String ip; + + /* + * Name of the pool this member is attached to. + */ + @XmlElement(name="poolname") + private String poolName; + + /* + * Status (active/inactive) + */ + @XmlElement + private String status; + + /** + * Private constructor used for JAXB mapping + */ + @SuppressWarnings("unused") + private PoolMember() {} + + public PoolMember(String name, String memberIP, String poolName){ + this.name = name; + this.ip = memberIP; + this.poolName = poolName; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the ip + */ + public String getIp() { + return ip; + } + + /** + * @param ip the ip to set + */ + public void setIp(String ip) { + this.ip = ip; + } + + /** + * @return the poolName + */ + public String getPoolName() { + return poolName; + } + + /** + * @param poolName the poolName to set + */ + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((ip == null) ? 0 : ip.hashCode()); + result = prime * result + + ((poolName == null) ? 0 : poolName.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PoolMember)) { + return false; + } + PoolMember other = (PoolMember) obj; + if (ip == null) { + if (other.ip != null) { + return false; + } + }else if (!ip.equals(other.ip)) { + return false; + } + if (poolName == null) { + if (other.poolName != null) { + return false; + } + }else if (!poolName.equals(other.poolName)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "PoolMember [name=" + name + ", ip=" + ip + ", poolName=" + + poolName + ", status=" + status + "]"; + } +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/VIP.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/VIP.java new file mode 100644 index 0000000000..6f866fe23f --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/entities/VIP.java @@ -0,0 +1,200 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.entities; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * This class represents the Virtual IP (VIP) address exposed by the load balancer application. + * Load balancer service differentiates one VIP from the other, using the following three properties: + * 1. IP address of the VIP exposed by the application + * 2. Protocol of the network traffic (TCP/UDP) + * 3. Port to which incoming traffic is destined + * + * User is allowed to create mutliple VIPs with the same IP, but all such VIPs (with the same IP) + * should differ at least in the protocol or port or both. + * + * NOTE: Each VIP should have a unique name. + */ +@XmlRootElement(name="vip") +@XmlAccessorType(XmlAccessType.NONE) + +public class VIP { + + /* + * Unique name of the VIP + */ + @XmlElement + private String name; + + /* + * Virtual IP address of the VIP + */ + @XmlElement + private String ip; + + /* + * Network traffic protocol + */ + @XmlElement + private String protocol; + + /* + * Port where network traffic is destined (destination port) + */ + @XmlElement + private short port; + + /* + * Name of the pool attached to the VIP for load balancing its traffic + */ + @XmlElement(name="poolname") + private String poolName; + + /* + * Status (Active/inactive) + */ + @XmlElement + private String status; + + /** + * Private constructor used for JAXB mapping + */ + @SuppressWarnings("unused") + private VIP() {} + + public VIP(String name, + String ip, + String protocol, + short port, + String poolName){ + this.name = name; + this.ip=ip; + this.protocol=protocol; + this.port = port; + this.poolName = poolName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public short getPort() { + return port; + } + + public void setPort(short port) { + this.port = port; + } + + public String getPoolName() { + return poolName; + } + + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((ip == null) ? 0 : ip.hashCode()); + result = prime * result + port; + result = prime * result + + ((protocol == null) ? 0 : protocol.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + + } + if (obj == null) { + return false; + } + if (!(obj instanceof VIP)) { + return false; + } + + VIP other = (VIP) obj; + if (ip == null) { + if (other.ip != null) { + return false; + } + }else if (!ip.equals(other.ip)) { + return false; + } + if (port != other.port) { + return false; + } + if (protocol == null) { + if (other.protocol != null) { + return false; + } + }else if (!protocol.equalsIgnoreCase(other.protocol)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "VIP [name=" + name + ", ip=" + ip + ", protocol=" + protocol + + ", port=" + port + ", poolName=" + poolName + ", status=" + + status + "]"; + } +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/Activator.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/Activator.java new file mode 100644 index 0000000000..0568fced6e --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/Activator.java @@ -0,0 +1,138 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.internal; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.felix.dm.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager; +import org.opendaylight.controller.hosttracker.IfIptoHost; +import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase; +import org.opendaylight.controller.sal.packet.IDataPacketService; +import org.opendaylight.controller.sal.packet.IListenDataPacket; +import org.opendaylight.controller.sal.routing.IRouting; +import org.opendaylight.controller.samples.loadbalancer.IConfigManager; + +/** + * Main application activator class for registering the dependencies and + * initialising the load balancer application. + * + */ + +public class Activator extends ComponentActivatorAbstractBase { + + /* + * Logger instance + */ + protected static final Logger logger = LoggerFactory.getLogger(Activator.class); + + /** + * Function called when the activator starts just after some + * initializations are done by the + * ComponentActivatorAbstractBase. + * + */ + public void init() { + } + + /** + * Function called when the activator stops just before the + * cleanup done by ComponentActivatorAbstractBase + * + */ + public void destroy() { + } + + /** + * Function that is used to communicate to dependency manager the + * list of known implementations for services inside a container + * + * + * @return An array containing all the CLASS objects that will be + * instantiated in order to get an fully working implementation + * Object + */ + public Object[] getImplementations() { + Object[] res = { LoadBalancerService.class }; + return res; + } + + /** + * Function that is called when configuration of the dependencies + * is required. + * + * @param c dependency manager Component object, used for + * configuring the dependencies exported and imported + * @param imp Implementation class that is being configured, + * needed as long as the same routine can configure multiple + * implementations + * @param containerName The containerName being configured, this allow + * also optional per-container different behavior if needed, usually + * should not be the case though. + */ + public void configureInstance(Component c, Object imp, String containerName) { + if (imp.equals(LoadBalancerService.class)) { + // export the service + Dictionary props = new Hashtable(); + props.put("salListenerName", "loadbalancer"); + + c.setInterface(new String[] { IListenDataPacket.class.getName(), + IConfigManager.class.getName()}, props); + + c.add(createContainerServiceDependency(containerName).setService( + IDataPacketService.class).setCallbacks( + "setDataPacketService", "unsetDataPacketService") + .setRequired(true)); + + c.add(createContainerServiceDependency(containerName).setService( + IRouting.class).setCallbacks("setRouting", "unsetRouting") + .setRequired(false)); + + c.add(createContainerServiceDependency(containerName).setService( + IfIptoHost.class).setCallbacks("setHostTracker", + "unsetHostTracker").setRequired(true)); + + c.add(createContainerServiceDependency(containerName).setService( + IForwardingRulesManager.class).setCallbacks( + "setForwardingRulesManager", "unsetForwardingRulesManager") + .setRequired(true)); + } + } + + /** + * Method which tells how many Global implementations are + * supported by the bundle. This way we can tune the number of + * components created. This components will be created ONLY at the + * time of bundle startup and will be destroyed only at time of + * bundle destruction, this is the major difference with the + * implementation retrieved via getImplementations where all of + * them are assumed to be in a container ! + * + * + * @return The list of implementations the bundle will support, + * in Global version + */ + protected Object[] getGlobalImplementations() { + return null; + } + + /** + * Configure the dependency for a given instance Global + * + * @param c Component assigned for this instance, this will be + * what will be used for configuration + * @param imp implementation to be configured + * @param containerName container on which the configuration happens + */ + protected void configureGlobalInstance(Component c, Object imp) { + } +} diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerService.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerService.java new file mode 100644 index 0000000000..1c69be895a --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerService.java @@ -0,0 +1,533 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.internal; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.Set; + +import org.apache.felix.dm.Component; +import org.opendaylight.controller.forwardingrulesmanager.FlowEntry; +import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager; +import org.opendaylight.controller.hosttracker.IfIptoHost; +import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; +import org.opendaylight.controller.sal.action.Action; +import org.opendaylight.controller.sal.action.Output; +import org.opendaylight.controller.sal.action.SetDlDst; +import org.opendaylight.controller.sal.action.SetDlSrc; +import org.opendaylight.controller.sal.action.SetNwDst; +import org.opendaylight.controller.sal.action.SetNwSrc; +import org.opendaylight.controller.sal.core.Node; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.Path; +import org.opendaylight.controller.sal.flowprogrammer.Flow; +import org.opendaylight.controller.sal.match.Match; +import org.opendaylight.controller.sal.match.MatchType; +import org.opendaylight.controller.sal.packet.Ethernet; +import org.opendaylight.controller.sal.packet.IDataPacketService; +import org.opendaylight.controller.sal.packet.IListenDataPacket; +import org.opendaylight.controller.sal.packet.IPv4; +import org.opendaylight.controller.sal.packet.Packet; +import org.opendaylight.controller.sal.packet.PacketResult; +import org.opendaylight.controller.sal.packet.RawPacket; +import org.opendaylight.controller.sal.routing.IRouting; +import org.opendaylight.controller.sal.utils.EtherTypes; +import org.opendaylight.controller.sal.utils.GlobalConstants; +import org.opendaylight.controller.sal.utils.IPProtocols; +import org.opendaylight.controller.samples.loadbalancer.ConfigManager; +import org.opendaylight.controller.samples.loadbalancer.IConfigManager; +import org.opendaylight.controller.samples.loadbalancer.LBConst; +import org.opendaylight.controller.samples.loadbalancer.LBUtil; +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.opendaylight.controller.samples.loadbalancer.policies.RandomLBPolicy; +import org.opendaylight.controller.samples.loadbalancer.policies.RoundRobinLBPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is the main class that represents the load balancer service. + * This is a sample load balancer application that balances traffic to backend servers + * based on the source address and source port on each incoming packet. The service + * reactively installs OpenFlow rules to direct all packets with a specific source address + * and source port to one of the appropriate backend servers. The servers may be chosen + * using a round robin policy or a random policy. This service can be configured via a + * REST APIs which are similar to the OpenStack Quantum LBaaS (Load-balancer-as-a-Service) + * v1.0 API proposal (http://wiki.openstack.org/Quantum/LBaaS) + * + * To use this service, a virtual IP (or VIP) should be exposed to the clients of this service + * and used as the destination address. A VIP is a entity that comprises of a virtual IP, port + * and protocol (TCP or UDP). + * Assumptions: + * 1. One or more VIPs may be mapped to the same server pool. All VIPs that share the same + * pool must also share the same load balancing policy (random or round robin). + * + * 2. Only one server pool can be be assigned to a VIP. + * + * 3. All flow rules are installed with an idle timeout of 5 seconds. + * + * 4. Packets to a VIP must leave the OpenFlow cluster from the same switch from where + * it entered it. + * + * 5. When you delete a VIP or a server pool or a server from a pool, the service does not + * delete the flow rules it has already installed. The flow rules should automatically + * time out after the idle timeout of 5 seconds. + * + */ +public class LoadBalancerService implements IListenDataPacket, IConfigManager{ + + /* + * Logger instance + */ + private static Logger lbsLogger = LoggerFactory.getLogger(LoadBalancerService.class); + + /* + * Single instance of the configuration manager. Application passes this reference to all + * the new policies implemented for load balancing. + */ + private static ConfigManager configManager = new ConfigManager(); + + /* + * Round robing policy instance. Need to implement factory patterns to get + * policy instance. + */ + private static RoundRobinLBPolicy rrLBMethod= new RoundRobinLBPolicy(configManager); + + /* + * Random policy instance. + */ + private static RandomLBPolicy ranLBMethod= new RandomLBPolicy(configManager); + + /* + * Reference to the data packet service + */ + private IDataPacketService dataPacketService = null; + + /* + * Reference to the host tracker service + */ + private IfIptoHost hostTracker; + + /* + * Reference to the forwarding manager + */ + private IForwardingRulesManager ruleManager; + + /* + * Reference to the routing service + */ + private IRouting routing; + + /* + * Load balancer application installs all flows with priority 2. + */ + private static short LB_IPSWITCH_PRIORITY = 2; + + /* + * Name of the container where this application will register. + */ + private String containerName = null; + + /* + * Set/unset methods for the service instance that load balancer + * service requires + */ + public String getContainerName() { + if (containerName == null) + return GlobalConstants.DEFAULT.toString(); + return containerName; + } + + void setDataPacketService(IDataPacketService s) { + this.dataPacketService = s; + } + + void unsetDataPacketService(IDataPacketService s) { + if (this.dataPacketService == s) { + this.dataPacketService = null; + } + } + + public void setRouting(IRouting routing) { + this.routing = routing; + } + + public void unsetRouting(IRouting routing) { + if (this.routing == routing) { + this.routing = null; + } + } + + public void setHostTracker(IfIptoHost hostTracker) { + lbsLogger.debug("Setting HostTracker"); + this.hostTracker = hostTracker; + } + + public void unsetHostTracker(IfIptoHost hostTracker) { + if (this.hostTracker == hostTracker) { + this.hostTracker = null; + } + } + + public void setForwardingRulesManager( + IForwardingRulesManager forwardingRulesManager) { + lbsLogger.debug("Setting ForwardingRulesManager"); + this.ruleManager = forwardingRulesManager; + } + + public void unsetForwardingRulesManager( + IForwardingRulesManager forwardingRulesManager) { + if (this.ruleManager == forwardingRulesManager) { + this.ruleManager = null; + } + } + + /** + * This method receives first packet of flows for which there is no + * matching flow rule installed on the switch. IP addresses used for VIPs + * are not supposed to be used by any real/virtual host in the network. + * Hence, any forwarding/routing service will not install any flows rules matching + * these VIPs. This ensures that all the flows destined for VIPs will not find a match + * in the switch and will be forwarded to the load balancing service. + * Service will decide where to route this traffic based on the load balancing + * policy of the VIP's attached pool and will install appropriate flow rules + * in a reactive manner. + */ + @Override + public PacketResult receiveDataPacket(RawPacket inPkt){ + + if (inPkt == null) { + return PacketResult.IGNORED; + } + + Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt); + + if (formattedPak instanceof Ethernet) { + byte[] vipMacAddr = ((Ethernet) formattedPak).getDestinationMACAddress(); + Object ipPkt = formattedPak.getPayload(); + + if (ipPkt instanceof IPv4) { + + lbsLogger.debug("Packet recieved from switch : {}",inPkt.getIncomingNodeConnector().getNode().toString()); + IPv4 ipv4Pkt = (IPv4)ipPkt; + if(IPProtocols.getProtocolName(ipv4Pkt.getProtocol()).equals(IPProtocols.TCP.toString()) + || IPProtocols.getProtocolName(ipv4Pkt.getProtocol()).equals(IPProtocols.TCP.toString())){ + + lbsLogger.debug("Packet protocol : {}",IPProtocols.getProtocolName(ipv4Pkt.getProtocol())); + Client client = new LBUtil().getClientFromPacket(ipv4Pkt); + VIP vip = new LBUtil().getVIPFromPacket(ipv4Pkt); + + if(configManager.vipExists(vip)){ + VIP vipWithPoolName = configManager.getVIPWithPoolName(vip); + String poolMemberIp = null; + if(configManager.getPool(vipWithPoolName.getPoolName()).getLbMethod().equalsIgnoreCase(LBConst.ROUND_ROBIN_LB_METHOD)){ + + poolMemberIp = rrLBMethod.getPoolMemberForClient(client,vipWithPoolName); + } + + if(configManager.getPool(vipWithPoolName.getPoolName()).getLbMethod().equalsIgnoreCase(LBConst.RANDOM_LB_METHOD)){ + poolMemberIp = ranLBMethod.getPoolMemberForClient(client,vipWithPoolName); + } + + try { + + Node clientNode = inPkt.getIncomingNodeConnector().getNode(); + HostNodeConnector hnConnector = this.hostTracker.hostFind(InetAddress.getByName(poolMemberIp)); + + Node destNode = hnConnector.getnodeconnectorNode(); + + lbsLogger.debug("Client is connected to switch : {}",clientNode.toString()); + lbsLogger.debug("Destination pool machine is connected to switch : {}",destNode.toString()); + + //Get path between both the nodes + Path route = this.routing.getRoute(clientNode, destNode); + + lbsLogger.info("Path between source (client) and destination switch nodes : {}",route.toString()); + + NodeConnector forwardPort = route.getEdges().get(0).getTailNodeConnector(); + + if(installLoadBalancerFlow(client, + vip, + clientNode, + poolMemberIp, + hnConnector.getDataLayerAddressBytes(), + forwardPort, + LBConst.FORWARD_DIRECTION_LB_FLOW)){ + lbsLogger.info("Traffic from client : {} will be routed " + + "to pool machine : {}",client,poolMemberIp); + }else{ + lbsLogger.error("Not able to route traffic from client : {}",client ); + } + + if(installLoadBalancerFlow(client, + vip, + clientNode, + poolMemberIp, + vipMacAddr, + inPkt.getIncomingNodeConnector(), + LBConst.REVERSE_DIRECTION_LB_FLOW)){ + lbsLogger.info("Flow rule installed to change the source ip/mac from " + + "pool machine ip {} to VIP {} for traffic coming pool machine",poolMemberIp,vip); + }else{ + lbsLogger.error("Not able to route traffic from client : {}",client ); + } + }catch (UnknownHostException e) { + lbsLogger.error("Pool member not found in the network : {}",e.getMessage()); + e.printStackTrace(); + } + } + } + } + } + return PacketResult.IGNORED; + } + + /* + * This method installs the flow rule for routing the traffic between two hosts. + * @param source Traffic is sent by this source + * @param dest Traffic is destined to this destination (VIP) + * @param sourceSwitch Switch from where controller received the packet + * @param destMachineIp IP address of the pool member where traffic needs to be routed + * @param destMachineMac MAC address of the pool member where traffic needs to be routed + * @param outport Use this port to send out traffic + * @param flowDirection FORWARD_DIRECTION_LB_FLOW or REVERSE_DIRECTION_LB_FLOW + * @return true If flow installation was successful + * false else + * @throws UnknownHostException + */ + private boolean installLoadBalancerFlow(Client source, + VIP dest, + Node sourceSwitch, + String destMachineIp, + byte[] destMachineMac, + NodeConnector outport, + int flowDirection) throws UnknownHostException{ + + Match match = new Match(); + List actions = new ArrayList(); + + if(flowDirection == LBConst.FORWARD_DIRECTION_LB_FLOW){ + match.setField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue()); + match.setField(MatchType.NW_SRC, InetAddress.getByName(source.getIp())); + match.setField(MatchType.NW_DST, InetAddress.getByName(dest.getIp())); + match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(dest.getProtocol())); + match.setField(MatchType.TP_SRC, source.getPort()); + match.setField(MatchType.TP_DST, dest.getPort()); + + actions.add(new SetNwDst(InetAddress.getByName(destMachineIp))); + actions.add(new SetDlDst(destMachineMac)); + } + + if(flowDirection == LBConst.REVERSE_DIRECTION_LB_FLOW){ + match.setField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue()); + match.setField(MatchType.NW_SRC, InetAddress.getByName(destMachineIp)); + match.setField(MatchType.NW_DST, InetAddress.getByName(source.getIp())); + match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(source.getProtocol())); + match.setField(MatchType.TP_SRC, dest.getPort()); + match.setField(MatchType.TP_DST,source.getPort()); + + actions.add(new SetNwSrc(InetAddress.getByName(dest.getIp()))); + actions.add(new SetDlSrc(destMachineMac)); + } + + actions.add(new Output(outport)); + + // Make sure the priority for IP switch entries is + // set to a level just above default drop entries + + Flow flow = new Flow(match, actions); + flow.setIdleTimeout((short) 5); + flow.setHardTimeout((short) 0); + flow.setPriority(LB_IPSWITCH_PRIORITY); + + String policyName = source.getIp()+":"+source.getProtocol()+":"+source.getPort(); + String flowName =null; + + if(flowDirection == LBConst.FORWARD_DIRECTION_LB_FLOW){ + flowName = "["+policyName+":"+source.getIp() + ":"+dest.getIp()+"]"; + } + + if(flowDirection == LBConst.REVERSE_DIRECTION_LB_FLOW){ + + flowName = "["+policyName+":"+dest.getIp() + ":"+source.getIp()+"]"; + } + + FlowEntry fEntry = new FlowEntry(policyName, flowName, flow, sourceSwitch); + + lbsLogger.info("Install flow entry {} on node {}",fEntry.toString(),sourceSwitch.toString()); + + if(!this.ruleManager.checkFlowEntryConflict(fEntry)){ + if(this.ruleManager.installFlowEntry(fEntry).isSuccess()){ + return true; + }else{ + lbsLogger.error("Error in installing flow entry to node : {}",sourceSwitch); + } + }else{ + lbsLogger.error("Conflicting flow entry exists : {}",fEntry.toString()); + } + return false; + } + + /** + * Function called by the dependency manager when all the required + * dependencies are satisfied + * + */ + void init(Component c) { + Dictionary props = c.getServiceProperties(); + if (props != null) { + this.containerName = (String) props.get("containerName"); + + lbsLogger.info("Running container name:" + this.containerName); + }else { + + // In the Global instance case the containerName is empty + this.containerName = ""; + } + lbsLogger.info(configManager.toString()); + } + + /** + * Function called by the dependency manager when at least one + * dependency become unsatisfied or when the component is shutting + * down because for example bundle is being stopped. + * + */ + void destroy() { + } + + /** + * Function called by dependency manager after "init ()" is called + * and after the services provided by the class are registered in + * the service registry + * + */ + void start() { + } + + /** + * Function called by the dependency manager before the services + * exported by the component are unregistered, this will be + * followed by a "destroy ()" calls + * + */ + void stop() { + } + + /* + * All the methods below are just proxy methods to direct the REST API requests to configuration + * manager. We need this redirection as currently, opendaylight supports only one + * implementation of the service. + */ + @Override + public Set getAllVIPs() { + return configManager.getAllVIPs(); + } + + @Override + public boolean vipExists(String name, String ip, String protocol, + short protocolPort, String poolName) { + return configManager.vipExists(name, ip, protocol, protocolPort, poolName); + } + + @Override + public boolean vipExists(VIP vip) { + return configManager.vipExists(vip); + } + + @Override + public VIP createVIP(String name, String ip, String protocol, + short protocolPort, String poolName) { + return configManager.createVIP(name, ip, protocol, protocolPort, poolName); + } + + @Override + public VIP updateVIP(String name, String poolName) { + return configManager.updateVIP(name, poolName); + } + + @Override + public VIP deleteVIP(String name) { + return configManager.deleteVIP(name); + } + + @Override + public boolean memberExists(String name, String memberIP, String poolName) { + return configManager.memberExists(name, memberIP, poolName); + } + + @Override + public Set getAllPoolMembers(String poolName) { + + return configManager.getAllPoolMembers(poolName); + } + + @Override + public PoolMember addPoolMember(String name, + String memberIP, + String poolName) { + return configManager.addPoolMember(name, memberIP, poolName); + } + + @Override + public PoolMember removePoolMember(String name, String poolName) { + + return configManager.removePoolMember(name, poolName); + } + + @Override + public Set getAllPools() { + + return configManager.getAllPools(); + } + + @Override + public Pool getPool(String poolName) { + return configManager.getPool(poolName); + } + + @Override + public boolean poolExists(String name, String lbMethod) { + return configManager.poolExists(name, lbMethod); + } + + @Override + public Pool createPool(String name, String lbMethod) { + return configManager.createPool(name, lbMethod); + } + + @Override + public Pool deletePool(String poolName) { + return configManager.deletePool(poolName); + } + + @Override + public boolean vipExists(String name) { + return configManager.vipExists(name); + } + + @Override + public boolean memberExists(String name, String poolName) { + return configManager.memberExists(name, poolName); + } + + @Override + public boolean poolExists(String name) { + return configManager.poolExists(name); + } + + @Override + public String getVIPAttachedPool(String name) { + return configManager.getVIPAttachedPool(name); + } +} diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/ILoadBalancingPolicy.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/ILoadBalancingPolicy.java new file mode 100644 index 0000000000..d69c63c200 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/ILoadBalancingPolicy.java @@ -0,0 +1,29 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.policies; + +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; + +/** + * All new load balancer policies must implement this interface. + */ +public interface ILoadBalancingPolicy { + + /** + * Returns IP address of the next pool member from the pool + * to which the load balancer service can direct incoming packets. + * @param source source on the packet + * @param dest virtual IP (VIP) that is used as destination on the packet + * @return IP address of the next pool member which will serve + * all incoming traffic destined for the given VIP and with the given source + * information + */ + public String getPoolMemberForClient(Client source, VIP dest); + +} \ No newline at end of file diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RandomLBPolicy.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RandomLBPolicy.java new file mode 100644 index 0000000000..b4b616f3a8 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RandomLBPolicy.java @@ -0,0 +1,108 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.policies; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +import org.opendaylight.controller.samples.loadbalancer.ConfigManager; +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements the random load balancing policy. + * + */ +public class RandomLBPolicy implements ILoadBalancingPolicy { + + /* + * Instance logger + */ + private static final Logger rLogger = LoggerFactory.getLogger(RandomLBPolicy.class); + + /* + * Reference to the configuration manager. This reference is passed from load balancer + * class. + */ + private ConfigManager cmgr; + + /* + * Mapping between the client and the pool member that serves all traffic for that client. + */ + private HashMap clientMemberMap; + + /* + * Random generator + */ + Random randomGenerator = null; + + @SuppressWarnings("unused") + private RandomLBPolicy(){} + + public RandomLBPolicy(ConfigManager cmgr){ + this.cmgr = cmgr; + this.clientMemberMap = new HashMap(); + randomGenerator = new Random(); + } + @Override + public String getPoolMemberForClient(Client source, VIP dest){ + + rLogger.info("Received traffic from client : {} for VIP : {} ",source, dest); + + syncWithLoadBalancerData(); + + PoolMember pm= null; + + if(this.clientMemberMap.containsKey(source)){ + pm= this.clientMemberMap.get(source); + rLogger.info("Client {} had sent traffic before,new traffic will be routed to the same pool member {}",source,pm); + }else{ + Pool pool = null; + pool = this.cmgr.getPool(dest.getPoolName()); + int memberNum = this.randomGenerator.nextInt(pool.getAllMembers().size()-1); + pm = pool.getAllMembers().get(memberNum); + this.clientMemberMap.put(source, pm ); + rLogger.info("Network traffic from client {} will be directed to pool member {}",pm); + } + return pm.getIp(); + } + + /* + * This method does the clean up. Whenever a new client packet arrives with a given VIP, + * this method checks the current configuration to see if any pool members have been deleted and + * cleans up the metadata stored by this loadbalancing algorithm. + */ + private void syncWithLoadBalancerData(){ + rLogger.debug("[Client - PoolMember] table before cleanup : {}",this.clientMemberMap.toString()); + + ArrayList removeClient = new ArrayList(); + + if(this.clientMemberMap.size() != 0){ + for(Client client : this.clientMemberMap.keySet()){ + + if(!this.cmgr.memberExists(this.clientMemberMap.get(client).getName(), + this.clientMemberMap.get(client).getPoolName())){ + removeClient.add(client); + } + } + } + + for(Client client : removeClient){ + this.clientMemberMap.remove(client); + + rLogger.debug("Removed client : {} ",client); + } + rLogger.debug("[Client - PoolMember] table after cleanup : {}",this.clientMemberMap.toString()); + } + +} diff --git a/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RoundRobinLBPolicy.java b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RoundRobinLBPolicy.java new file mode 100644 index 0000000000..f1011dddc5 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/policies/RoundRobinLBPolicy.java @@ -0,0 +1,153 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.policies; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.opendaylight.controller.samples.loadbalancer.ConfigManager; +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements the round robin load balancing policy. + * + */ +public class RoundRobinLBPolicy implements ILoadBalancingPolicy{ + + /* + * Logger instance + */ + private static final Logger rrLogger = LoggerFactory.getLogger(RoundRobinLBPolicy.class); + + /* + * Reference to the configuration manager. This reference is passed from load balancer + * class. + */ + private ConfigManager cmgr; + + /* + * Mapping between the client and the pool member that serves all traffic for that client. + */ + private HashMap clientMemberMap; + + /* + * Maintains the next pool member counter for the VIPs. + * More than one VIP can be attached to one pool, so each VIP + * will have its own counter for the next pool member from + * the same pool. + */ + private HashMap nextItemFromPool; + + @SuppressWarnings("unused") + private RoundRobinLBPolicy(){} + + public RoundRobinLBPolicy(ConfigManager cmgr){ + this.cmgr = cmgr; + this.clientMemberMap = new HashMap(); + this.nextItemFromPool = new HashMap(); + } + + @Override + public String getPoolMemberForClient(Client source, VIP dest){ + + rrLogger.info("Received traffic from client : {} for VIP : {} ",source, dest); + + syncWithLoadBalancerData(); + + PoolMember pm= null; + + if(this.clientMemberMap.containsKey(source)){ + + pm= this.clientMemberMap.get(source); + rrLogger.info("Client {} had sent traffic before,new traffic will be routed to the same pool member {}",source,pm); + }else{ + + Pool pool = null; + if(nextItemFromPool.containsKey(dest)){ + + int memberNum = nextItemFromPool.get(dest).intValue(); + rrLogger.debug("Packet is from new client for VIP {}",dest); + pool = this.cmgr.getPool(dest.getPoolName()); + pm = pool.getAllMembers().get(memberNum); + this.clientMemberMap.put(source, pm ); + rrLogger.info("New client's packet will be directed to pool member {}",pm); + memberNum++; + + if(memberNum > pool.getAllMembers().size()-1){ + memberNum = 0; + } + rrLogger.debug("Next pool member for new client of VIP is set to {}",pool.getAllMembers().get(memberNum)); + + this.nextItemFromPool.put(dest, new Integer(memberNum)); + }else{ + rrLogger.debug("Network traffic for VIP : {} has appeared first time from client {}",dest,source); + pool = this.cmgr.getPool(dest.getPoolName()); + pm = pool.getAllMembers().get(0); + this.clientMemberMap.put(source, pm); + + rrLogger.info("Network traffic from client {} will be directed to pool member {}",pm); + this.nextItemFromPool.put(dest, new Integer(1)); + rrLogger.debug("Next pool member for new client of VIP is set to {}",pool.getAllMembers().get(1)); + } + } + return pm.getIp(); + } + + /* + * This method does the clean up. Whenever a new client packet arrives with a given VIP, + * this method checks the current configuration to see if any pool members have been deleted and + * cleans up the metadata stored by this loadbalancing algorithm. + */ + private void syncWithLoadBalancerData(){ + rrLogger.debug("[Client - PoolMember] table before cleanup : {}",this.clientMemberMap.toString()); + ArrayList removeClient = new ArrayList(); + + if(this.clientMemberMap.size() != 0){ + for(Client client : this.clientMemberMap.keySet()){ + if(!this.cmgr.memberExists(this.clientMemberMap.get(client).getName(), + this.clientMemberMap.get(client).getPoolName())){ + + removeClient.add(client); + } + } + } + + for(Client client : removeClient){ + this.clientMemberMap.remove(client); + + rrLogger.debug("Removed client : {} ",client); + } + rrLogger.debug("[Client - PoolMember] table after cleanup : {}",this.clientMemberMap.toString()); + + rrLogger.debug("[VIP- NextMember] table before cleanup : {}",this.nextItemFromPool.toString()); + + ArrayList resetVIPPoolMemberCount= new ArrayList(); + + if(this.nextItemFromPool.size() != 0){ + + for(VIP vip:this.nextItemFromPool.keySet()){ + if(this.nextItemFromPool.get(vip).intValue() > this.cmgr.getPool(vip.getPoolName()).getAllMembers().size()-1){ + + resetVIPPoolMemberCount.add(vip); + } + } + } + + for(VIP vip:resetVIPPoolMemberCount){ + rrLogger.debug("VIP next pool member counter reset to 0"); + this.nextItemFromPool.put(vip, new Integer(0)); + } + + rrLogger.debug("[VIP- NextMember] table after cleanup : {}",this.nextItemFromPool.toString()); + } +} diff --git a/opendaylight/samples/loadbalancer/src/test/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerTest.java b/opendaylight/samples/loadbalancer/src/test/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerTest.java new file mode 100644 index 0000000000..f8633b9ef8 --- /dev/null +++ b/opendaylight/samples/loadbalancer/src/test/java/org/opendaylight/controller/samples/loadbalancer/internal/LoadBalancerTest.java @@ -0,0 +1,69 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.internal; + + +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.controller.samples.loadbalancer.ConfigManager; +import org.opendaylight.controller.samples.loadbalancer.entities.Client; +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.opendaylight.controller.samples.loadbalancer.policies.RoundRobinLBPolicy; + +import junit.framework.TestCase; + +/** + * + * Class to unit test the load balancing policies. + * + */ +public class LoadBalancerTest extends TestCase { + @Test + public void testRoundRobinPolicy() { + ConfigManager cm = null; + cm = new ConfigManager(); + Assert.assertFalse(cm== null); + + Pool pool = cm.createPool("TestPool","roundrobin"); + VIP vip = cm.createVIP("TestVIP","10.0.0.9","TCP",(short)5550,"TestPool"); + PoolMember host1 = new PoolMember("host1","10.0.0.1","TestPool"); + PoolMember host2 = new PoolMember("host2","10.0.0.2","TestPool"); + PoolMember host3 = new PoolMember("host3","10.0.0.3","TestPool"); + PoolMember host4 = new PoolMember("host4","10.0.0.4","TestPool"); + PoolMember host5 = new PoolMember("host5","10.0.0.5","TestPool"); + PoolMember host6 = new PoolMember("host6","10.0.0.6","TestPool"); + PoolMember host7 = new PoolMember("host7","10.0.0.7","TestPool"); + + pool.addMember(host1); + pool.addMember(host2); + pool.addMember(host3); + pool.addMember(host4); + pool.addMember(host5); + pool.addMember(host6); + pool.addMember(host7); + pool.addVIP(vip); + + Assert.assertTrue(cm.getAllPoolMembers("TestPool").size() == pool.getAllMembers().size()); + + RoundRobinLBPolicy rrp = new RoundRobinLBPolicy(cm); + + Client c1 = new Client("10.0.0.1","TCP",(short)5000); + Assert.assertTrue(rrp.getPoolMemberForClient(c1, vip).equals(host1.getIp())); + + c1 = new Client("10.0.0.1","TCP",(short)5001); + Assert.assertTrue(rrp.getPoolMemberForClient(c1, vip).equals(host2.getIp())); + + c1 = new Client("10.0.0.1","TCP",(short)5002); + Assert.assertTrue(rrp.getPoolMemberForClient(c1, vip).equals(host3.getIp())); + + c1 = new Client("10.0.0.1","TCP",(short)5003); + Assert.assertTrue(rrp.getPoolMemberForClient(c1, vip).equals(host4.getIp())); + } +} \ No newline at end of file diff --git a/opendaylight/samples/northbound/loadbalancer/enunciate.xml b/opendaylight/samples/northbound/loadbalancer/enunciate.xml new file mode 100644 index 0000000000..e666af7e51 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/enunciate.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/opendaylight/samples/northbound/loadbalancer/pom.xml b/opendaylight/samples/northbound/loadbalancer/pom.xml new file mode 100644 index 0000000000..2b9b1c448d --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../../commons/opendaylight + + org.opendaylight.controller + samples.loadbalancer.northbound + 0.4.0-SNAPSHOT + bundle + + + + + org.codehaus.enunciate + maven-enunciate-plugin + ${enunciate.version} + + + org.opendaylight.controller + sal + 0.4.0-SNAPSHOT + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + org.opendaylight.controller.samples.loadbalancer, + org.opendaylight.controller.samples.loadbalancer.entities, + org.opendaylight.controller.samples.loadbalancer.internal, + org.opendaylight.controller.samples.loadbalancer.policies, + org.opendaylight.controller.hosttracker, + org.opendaylight.controller.sal.core, + org.opendaylight.controller.sal.utils, + org.opendaylight.controller.containermanager, + org.opendaylight.controller.switchmanager, + org.apache.commons.logging, + com.sun.jersey.spi.container.servlet, + org.opendaylight.controller.northbound.commons, + org.opendaylight.controller.northbound.commons.exception, + com.sun.jersey.spi.spring.container.servlet, + org.springframework.web.context, + org.springframework.web, + org.springframework.web.servlet, + org.springframework.web.filter, + org.springframework.security.config, + org.springframework.security.web.authentication, + org.springframework.security.web.authentication.www, + javax.ws.rs, + javax.ws.rs.core, + javax.xml.bind.annotation, + javax.xml.bind, + org.slf4j, + !org.codehaus.enunciate.jaxrs + + /one/nb/v2/lb + + + + + + + + org.opendaylight.controller.thirdparty + com.sun.jersey.jersey-servlet + 1.17-SNAPSHOT + + + org.opendaylight.controller + containermanager + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + hosttracker + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + samples.loadbalancer + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + sal + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + commons.northbound + 0.4.0-SNAPSHOT + + + org.codehaus.enunciate + enunciate-core-annotations + ${enunciate.version} + + + diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthbound.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthbound.java new file mode 100644 index 0000000000..4ec567c95b --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthbound.java @@ -0,0 +1,471 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.northbound; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBElement; + +import org.codehaus.enunciate.jaxrs.ResponseCode; +import org.codehaus.enunciate.jaxrs.StatusCodes; +import org.codehaus.enunciate.jaxrs.TypeHint; +import org.opendaylight.controller.containermanager.IContainerManager; +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; +import org.opendaylight.controller.samples.loadbalancer.entities.PoolMember; +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; +import org.opendaylight.controller.northbound.commons.RestMessages; +import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException; +import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException; +import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException; +import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; +import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException; +import org.opendaylight.controller.northbound.commons.exception.UnsupportedMediaTypeException; +import org.opendaylight.controller.sal.utils.ServiceHelper; +import org.opendaylight.controller.samples.loadbalancer.IConfigManager; + +/** + * This class exposes North bound REST APIs for the Load Balancer Service. + * Following APIs are exposed by the Load Balancer Service: + * + * Data retrieval REST APIs:: + * 1. Get details of all existing pools + * Type : GET + * URI : /one/nb/v2/lb/{container-name}/ + * NOTE: Current implementation of the opendaylight usage 'default' as a container-name + * e.g : http://localhost:8080/one/nb/v2/lb/default will give you list of all the pools + * + * 2. Get details of all the existing VIPs + * Type : GET + * URI: /one/nb/v2/lb/{container-name}/vips + * + * Pool related REST APIs:: + * 1. Create Pool : + * Type : POST + * URI : /one/nb/v2/lb/{container-name}/create/pool + * Request body : + * { + * "name":"", + * "lbmethod":"" + * } + * Currently, two load balancing policies are allowed {"roundrobin" and "random" } + * + * 2. Delete Pool : + * Type : DELETE + * URI : /one/nb/v2/lb/{container-name}/delete/pool/{pool-name} + * + * VIP related REST APIs:: + * 1. Create VIP: + * Type : POST + * URI : /one/nb/v2/lb/{container-name}/create/vip + * Request body : + * { + * "name":"", + * "ip":"ip in (xxx.xxx.xxx.xxx) format", + * "protocol":"TCP/UDP", + * "port":"any valid port number", + * "poolname":"" (optional) + * } + * The pool name is optional and can be set up at a later stage (using the REST API given below). + * + * 2. Update VIP: Update pool name of the VIP + * Type : PUT + * URI : /one/nb/v2/lb/{container-name}/update/vip + * Request body : + * { + * "name":"", + * "poolname":"" + * } + * Currently, we only allow update of the VIP pool name (if a VIP does not have an attached pool) + * and not of the VIP name itself. + * The specified pool name must already exist. If the specified VIP is already attached to a pool, the update + * will fail. + * + * 3. Delete VIP : + * Type : DELETE + * URI : /one/nb/v2/lb/{container-name}/delete/vip/{vip-name} + * + * Pool member related REST APIs:: + * 1. Create pool member: + * Type : POST + * URI : /one/nb/v2/lb/default/create/poolmember + * Request body : + * { + * "name":"", + * "ip":"ip in (xxx.xxx.xxx.xxx) format", + * "poolname":"existing pool name" + * } + * + * 2. Delete pool member: + * Type : DELETE + * URI : /one/nb/v2/lb/{container-name}/delete/poolmember/{pool-member-name}/{pool-name} + * + * NOTE: Property "name" of each individual entity must be unique. + * All the above REST APIs throw appropriate response codes in case of error/success. + * Please consult the respective methods to get details of various response codes. + */ + +@Path("/") +public class LoadBalancerNorthbound { + + /* + * Method returns the Load balancer service instance running within + * 'default' container. + */ + private IConfigManager getConfigManagerService(String containerName) { + IContainerManager containerManager = (IContainerManager) ServiceHelper + .getGlobalInstance(IContainerManager.class, this); + if (containerManager == null) { + throw new ServiceUnavailableException("Container " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + boolean found = false; + List containerNames = containerManager.getContainerNames(); + for (String cName : containerNames) { + if (cName.trim().equalsIgnoreCase(containerName.trim())) { + found = true; + } + } + + if (found == false) { + throw new ResourceNotFoundException(containerName + " " + + RestMessages.NOCONTAINER.toString()); + } + + IConfigManager configManager = (IConfigManager) ServiceHelper.getInstance( + IConfigManager.class, containerName, this); + + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer" + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + return configManager; + } + + @Path("/{containerName}") + @GET + @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @TypeHint(Pools.class) + @StatusCodes( { + @ResponseCode(code = 200, condition = "Operation successful"), + @ResponseCode(code = 404, condition = "The containerName is not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable") }) + public Pools getAllPools( + @PathParam("containerName") String containerName) { + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + return new Pools(configManager.getAllPools()); + } + + @Path("/{containerName}/vips") + @GET + @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @TypeHint(VIPs.class) + @StatusCodes( { + @ResponseCode(code = 200, condition = "Operation successful"), + @ResponseCode(code = 404, condition = "The containerName is not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable") }) + public VIPs getAllVIPs( + @PathParam("containerName") String containerName) { + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + return new VIPs(configManager.getAllVIPs()); + } + + @Path("/{containerName}/create/vip") + @POST + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 201, condition = "VIP created successfully"), + @ResponseCode(code = 404, condition = "The Container Name not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 409, condition = "VIP already exist"), + @ResponseCode(code = 415, condition = "Invalid input data")}) + public Response addVIP(@PathParam("containerName") String containerName, + @TypeHint(VIP.class) JAXBElement inVIP){ + + VIP vipInput = inVIP.getValue(); + String name = vipInput.getName(); + String ip = vipInput.getIp(); + String protocol = vipInput.getProtocol(); + short protocolPort = vipInput.getPort(); + String poolName = vipInput.getPoolName(); + if(name.isEmpty() || + ip.isEmpty()|| + protocol.isEmpty()|| + protocolPort < 0 ){ + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + } + + IConfigManager configManager = getConfigManagerService(containerName); + + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.vipExists(name, ip, protocol, protocolPort, poolName)){ + + VIP vip = configManager.createVIP(name, ip, protocol, protocolPort, poolName); + if ( vip != null){ + return Response.status(Response.Status.CREATED).build(); + } + }else{ + throw new ResourceConflictException(NBConst.RES_VIP_ALREADY_EXIST); + } + throw new InternalServerErrorException(NBConst.RES_VIP_CREATION_FAILED); + } + + @Path("/{containerName}/update/vip") + @PUT + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 201, condition = "VIP updated successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "VIP not found"), + @ResponseCode(code = 404, condition = "Pool not found"), + @ResponseCode(code = 405, condition = "Pool already attached to the VIP"), + @ResponseCode(code = 415, condition = "Invalid input name")}) + public Response updateVIP(@PathParam("containerName") String containerName, + @TypeHint(VIP.class) JAXBElement inVIP) { + + VIP vipInput = inVIP.getValue(); + String name = vipInput.getName(); + String poolName = vipInput.getPoolName(); + if(name.isEmpty() || + poolName.isEmpty()){ + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + } + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.poolExists(poolName)) + throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND); + + if(configManager.getVIPAttachedPool(name)!=null) + throw new MethodNotAllowedException(NBConst.RES_VIP_POOL_EXIST); + + if(configManager.updateVIP(name, poolName)!= null) + return Response.status(Response.Status.ACCEPTED).build(); + + throw new InternalServerErrorException(NBConst.RES_VIP_UPDATE_FAILED); + } + + @Path("/{containerName}/delete/vip/{vipName}") + @DELETE + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 200, condition = "VIP deleted successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 404, condition = "VIP not found"), + @ResponseCode(code = 500, condition = "Failed to delete VIP")}) + public Response deleteVIP( + @PathParam(value = "containerName") String containerName, + @PathParam(value = "vipName") String vipName) { + + if(vipName.isEmpty()) + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer" + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.vipExists(vipName)) + throw new ResourceNotFoundException(NBConst.RES_VIP_NOT_FOUND); + + for(VIP vip : configManager.getAllVIPs()){ + if(vip.getName().equals(vipName)){ + configManager.deleteVIP(vipName); + return Response.ok().build(); + } + } + throw new InternalServerErrorException(NBConst.RES_VIP_DELETION_FAILED); + } + + @Path("/{containerName}/create/pool") + @POST + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 201, condition = "Pool created successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 409, condition = "Pool already exist"), + @ResponseCode(code = 415, condition = "Invalid input data")}) + public Response addPool(@PathParam("containerName") String containerName, + @TypeHint(Pool.class) JAXBElement inPool) { + + Pool poolInput = inPool.getValue(); + String name = poolInput.getName(); + String lbMethod =poolInput.getLbMethod(); + if(name.isEmpty() || + lbMethod.isEmpty()){ + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + } + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.poolExists(name)){ + + Pool pool = configManager.createPool(name, lbMethod); + if ( pool != null){ + return Response.status(Response.Status.CREATED).build(); + } + }else{ + throw new ResourceConflictException(NBConst.RES_POOL_ALREADY_EXIST); + } + throw new InternalServerErrorException(NBConst.RES_POOL_CREATION_FAILED); + } + + @Path("/{containerName}/delete/pool/{poolName}") + @DELETE + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 200, condition = "Pool deleted successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 404, condition = "Pool not found"), + @ResponseCode(code = 500, condition = "Failed to delete Pool")}) + public Response deletePool( + @PathParam(value = "containerName") String containerName, + @PathParam(value = "poolName") String poolName) { + + if(poolName.isEmpty()) + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer" + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.poolExists(poolName)) + throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND); + + for(Pool pool:configManager.getAllPools()){ + if(pool.getName().equals(poolName)){ + configManager.deletePool(poolName); + return Response.ok().build(); + } + } + throw new InternalServerErrorException(NBConst.RES_POOL_DELETION_FAILED); + } + + @Path("/{containerName}/create/poolmember") + @POST + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 201, condition = "Pool member created successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 404, condition = "Pool not found"), + @ResponseCode(code = 409, condition = "Pool member already exist"), + @ResponseCode(code = 415, condition = "Invalid input data")}) + public Response addPoolMember(@PathParam("containerName") String containerName, + @TypeHint(PoolMember.class) JAXBElement inPoolMember){ + + PoolMember pmInput = inPoolMember.getValue(); + String name = pmInput.getName(); + String memberIP = pmInput.getIp(); + String poolName = pmInput.getPoolName(); + + if(name.isEmpty() || + memberIP.isEmpty()|| + poolName.isEmpty()){ + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + } + + IConfigManager configManager = getConfigManagerService(containerName); + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.poolExists(poolName)) + throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND); + + if(!configManager.memberExists(name, memberIP, poolName)){ + + PoolMember poolMember = configManager.addPoolMember(name, memberIP, poolName); + if ( poolMember != null){ + return Response.status(Response.Status.CREATED).build(); + } + }else{ + throw new ResourceConflictException(NBConst.RES_POOLMEMBER_ALREADY_EXIST); + } + throw new InternalServerErrorException(NBConst.RES_POOLMEMBER_CREATION_FAILED); + } + + @Path("/{containerName}/delete/poolmember/{poolMemberName}/{poolName}") + @DELETE + @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @StatusCodes( { + @ResponseCode(code = 200, condition = "Pool member deleted successfully"), + @ResponseCode(code = 404, condition = "The containerName not found"), + @ResponseCode(code = 503, condition = "Load balancer service is unavailable"), + @ResponseCode(code = 404, condition = "Pool member not found"), + @ResponseCode(code = 404, condition = "Pool not found")}) + public Response deletePoolMember( + @PathParam(value = "containerName") String containerName, + @PathParam(value = "poolMemberName") String poolMemberName, + @PathParam(value = "poolName") String poolName) { + + if(poolMemberName.isEmpty()|| + poolName.isEmpty()) + throw new UnsupportedMediaTypeException(RestMessages.INVALIDDATA.toString()); + + IConfigManager configManager = getConfigManagerService(containerName); + + if (configManager == null) { + throw new ServiceUnavailableException("Load Balancer" + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + if(!configManager.poolExists(poolName)) + throw new ResourceNotFoundException(NBConst.RES_POOL_NOT_FOUND); + + if(configManager.memberExists(poolMemberName, poolName)){ + + configManager.removePoolMember(poolMemberName, poolName); + + return Response.ok().build(); + } + throw new ResourceNotFoundException(NBConst.RES_POOLMEMBER_NOT_FOUND); + } +} diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java new file mode 100644 index 0000000000..0c17c84ec4 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java @@ -0,0 +1,28 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.samples.loadbalancer.northbound; + +import java.util.HashSet; +import java.util.Set; +import javax.ws.rs.core.Application; + +/** + * This class is an instance of javax.ws.rs.core.Application and is used to return the classes + * that will be instantiated for JAXRS processing. This is necessary + * because package scanning in jersey doesn't yet work in OSGi environment. + * + */ +public class LoadBalancerNorthboundRSApplication extends Application { + @Override + public Set> getClasses() { + Set> classes = new HashSet>(); + classes.add(LoadBalancerNorthbound.class); + return classes; + } +} diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/NBConst.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/NBConst.java new file mode 100644 index 0000000000..4aa4705a53 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/NBConst.java @@ -0,0 +1,43 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.northbound; + +/** + * This class defines all the constants used by the load balancer north bound service + * + */ +public class NBConst { + + public static final String RES_VIP_ALREADY_EXIST= " VIP already exists"; + + public static final String RES_VIP_NOT_FOUND= " VIP not found"; + + public static final String RES_VIP_CREATION_FAILED = " Creation of VIP failed"; + + public static final String RES_VIP_DELETION_FAILED = " Deletion of VIP failed"; + + public static final String RES_VIP_UPDATE_FAILED = " Update of VIP failed"; + + public static final String RES_POOL_ALREADY_EXIST= " Pool already exists"; + + public static final String RES_POOL_NOT_FOUND= " Pool not found"; + + public static final String RES_POOL_CREATION_FAILED = " Creation of pool failed"; + + public static final String RES_POOL_DELETION_FAILED = " Deletion of pool failed"; + + public static final String RES_POOLMEMBER_ALREADY_EXIST= " Pool member already exists"; + + public static final String RES_POOLMEMBER_NOT_FOUND= " Pool member not found"; + + public static final String RES_POOLMEMBER_CREATION_FAILED = " Creation of pool member failed"; + + public static final String RES_POOLMEMBER_DELETION_FAILED = " Deletion of pool member failed"; + + public static final String RES_VIP_POOL_EXIST = "Pool already attached to a VIP"; +} diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/Pools.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/Pools.java new file mode 100644 index 0000000000..abb5495cbf --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/Pools.java @@ -0,0 +1,51 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.northbound; + +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.samples.loadbalancer.entities.Pool; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +/** + * JAX-RS resource for handling details of all the available pools + * in response to respective REST API requests. + */ + +public class Pools { + + @XmlElement (name="pool") + Set loadBalancerPools; + + public Pools() { + } + + public Pools (Set loadBalancerPools) { + this.loadBalancerPools = loadBalancerPools; + } + + /** + * @return the loadBalancerPools + */ + public Set getLoadBalancerPools() { + return loadBalancerPools; + } + + /** + * @param loadBalancerPools the loadBalancerPools to set + */ + public void setLoadBalancerPools(Set loadBalancerPools) { + this.loadBalancerPools = loadBalancerPools; + } +} diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/VIPs.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/VIPs.java new file mode 100644 index 0000000000..f0af929b39 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/VIPs.java @@ -0,0 +1,53 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.samples.loadbalancer.northbound; + +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.samples.loadbalancer.entities.VIP; + +/** + * JAX-RS resource for handling details of all the available VIPs + * in response to respective REST API requests. + */ + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) + +public class VIPs { + + @XmlElement (name="vip") + Set loadBalancerVIPs; + + public VIPs() {} + + + public VIPs (Set loadBalancerVIPs) { + this.loadBalancerVIPs = loadBalancerVIPs; + } + + /** + * @return the loadBalancerVIPs + */ + public Set getLoadBalancerVIPs() { + return loadBalancerVIPs; + } + + /** + * @param loadBalancerVIPs the loadBalancerVIPs to set + */ + + public void setLoadBalancerVIPs(Set loadBalancerVIPs) { + this.loadBalancerVIPs = loadBalancerVIPs; + } +} \ No newline at end of file diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.factories b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..93db02ec92 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.handlers b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..957af910d8 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.handlers @@ -0,0 +1,10 @@ +http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler +http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler +http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler +http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler +http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler +http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler +http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler +http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler +http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler +http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.schemas b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..d865edcfd2 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,49 @@ +http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd +http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd +http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd +http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd +http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd +http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd +http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd +http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd +http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd +http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd +http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd +http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd +http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd +http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd +http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd +http\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd +http\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd +http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd +http\://www.springframework.org/schema/context/spring-context-2.5.xsd=org/springframework/context/config/spring-context-2.5.xsd +http\://www.springframework.org/schema/context/spring-context-3.0.xsd=org/springframework/context/config/spring-context-3.0.xsd +http\://www.springframework.org/schema/context/spring-context-3.1.xsd=org/springframework/context/config/spring-context-3.1.xsd +http\://www.springframework.org/schema/context/spring-context-3.2.xsd=org/springframework/context/config/spring-context-3.2.xsd +http\://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context-3.2.xsd +http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd +http\://www.springframework.org/schema/jee/spring-jee-2.5.xsd=org/springframework/ejb/config/spring-jee-2.5.xsd +http\://www.springframework.org/schema/jee/spring-jee-3.0.xsd=org/springframework/ejb/config/spring-jee-3.0.xsd +http\://www.springframework.org/schema/jee/spring-jee-3.1.xsd=org/springframework/ejb/config/spring-jee-3.1.xsd +http\://www.springframework.org/schema/jee/spring-jee-3.2.xsd=org/springframework/ejb/config/spring-jee-3.2.xsd +http\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-3.2.xsd +http\://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd +http\://www.springframework.org/schema/lang/spring-lang-2.5.xsd=org/springframework/scripting/config/spring-lang-2.5.xsd +http\://www.springframework.org/schema/lang/spring-lang-3.0.xsd=org/springframework/scripting/config/spring-lang-3.0.xsd +http\://www.springframework.org/schema/lang/spring-lang-3.1.xsd=org/springframework/scripting/config/spring-lang-3.1.xsd +http\://www.springframework.org/schema/lang/spring-lang-3.2.xsd=org/springframework/scripting/config/spring-lang-3.2.xsd +http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-3.2.xsd +http\://www.springframework.org/schema/task/spring-task-3.0.xsd=org/springframework/scheduling/config/spring-task-3.0.xsd +http\://www.springframework.org/schema/task/spring-task-3.1.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd +http\://www.springframework.org/schema/task/spring-task-3.2.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd +http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd +http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd +http\://www.springframework.org/schema/cache/spring-cache-3.2.xsd=org/springframework/cache/config/spring-cache-3.2.xsd +http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-3.2.xsd +http\://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd=org/springframework/web/servlet/config/spring-mvc-3.0.xsd +http\://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd=org/springframework/web/servlet/config/spring-mvc-3.1.xsd +http\://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd=org/springframework/web/servlet/config/spring-mvc-3.2.xsd +http\://www.springframework.org/schema/mvc/spring-mvc.xsd=org/springframework/web/servlet/config/spring-mvc-3.2.xsd +http\://www.springframework.org/schema/security/spring-security-3.1.xsd=org/springframework/security/config/spring-security-3.1.xsd +http\://www.springframework.org/schema/security/spring-security-3.2.xsd=org/springframework/security/config/spring-security-3.2.xsd + diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.tooling b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.tooling new file mode 100644 index 0000000000..057d834289 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,39 @@ +# Tooling related information for the beans namespace +http\://www.springframework.org/schema/beans@name=beans Namespace +http\://www.springframework.org/schema/beans@prefix=beans +http\://www.springframework.org/schema/beans@icon=org/springframework/beans/factory/xml/spring-beans.gif + +# Tooling related information for the util namespace +http\://www.springframework.org/schema/util@name=util Namespace +http\://www.springframework.org/schema/util@prefix=util +http\://www.springframework.org/schema/util@icon=org/springframework/beans/factory/xml/spring-util.gif + +# Tooling related information for the context namespace +http\://www.springframework.org/schema/context@name=context Namespace +http\://www.springframework.org/schema/context@prefix=context +http\://www.springframework.org/schema/context@icon=org/springframework/context/config/spring-context.gif + +# Tooling related information for the jee namespace +http\://www.springframework.org/schema/jee@name=jee Namespace +http\://www.springframework.org/schema/jee@prefix=jee +http\://www.springframework.org/schema/jee@icon=org/springframework/ejb/config/spring-jee.gif + +# Tooling related information for the scheduling namespace +http\://www.springframework.org/schema/task@name=task Namespace +http\://www.springframework.org/schema/task@prefix=task +http\://www.springframework.org/schema/task@icon=org/springframework/scheduling/config/spring-task.gif + +# Tooling related information for the lang namespace +http\://www.springframework.org/schema/lang@name=lang Namespace +http\://www.springframework.org/schema/lang@prefix=lang +http\://www.springframework.org/schema/lang@icon=org/springframework/scripting/config/spring-lang.gif + +# Tooling related information for the cache namespace +http\://www.springframework.org/schema/cache@name=cache Namespace +http\://www.springframework.org/schema/cache@prefix=cache +http\://www.springframework.org/schema/cache@icon=org/springframework/cache/config/spring-cache.gif + +# Tooling related information for the mvc namespace +http\://www.springframework.org/schema/mvc@name=mvc Namespace +http\://www.springframework.org/schema/mvc@prefix=mvc +http\://www.springframework.org/schema/mvc@icon=org/springframework/web/servlet/config/spring-mvc.gif diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/context.xml b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/context.xml new file mode 100644 index 0000000000..8a4bda53b0 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/context.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/servlet/security.xml b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/servlet/security.xml new file mode 100644 index 0000000000..bce59ee3c5 --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/spring/servlet/security.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml new file mode 100644 index 0000000000..5011e748df --- /dev/null +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml @@ -0,0 +1,43 @@ + + + + JAXRSLoadBalancer + com.sun.jersey.spi.spring.container.servlet.SpringServlet + + javax.ws.rs.Application + org.opendaylight.controller.samples.loadbalancer.northbound.LoadBalancerNorthboundRSApplication + + 1 + + + + JAXRSLoadBalancer + /* + + + + + + org.springframework.web.context.ContextLoaderListener + + + + contextConfigLocation + /WEB-INF/spring/*.xml + + + + springSecurityFilterChain + + org.springframework.web.filter.DelegatingFilterProxy + + + + + springSecurityFilterChain + /* + + -- 2.36.6