From b88055852fd225d36bb65775a14130b74eb95da7 Mon Sep 17 00:00:00 2001
From: Diogo <diogo_572@hotmail.com>
Date: Mon, 20 Mar 2017 16:46:43 +0000
Subject: [PATCH] Load balance member picking through switch statistics is now
 possible

---
 .../loadbalancer/LBPool.java                  |  118 +-
 .../loadbalancer/LoadBalancer.java            | 1557 +++++++++--------
 .../loadbalancer/PoolsResource.java           |    7 +-
 .../resources/floodlightdefault.properties    |    2 +-
 4 files changed, 890 insertions(+), 794 deletions(-)

diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LBPool.java b/src/main/java/net/floodlightcontroller/loadbalancer/LBPool.java
index 1f842aea7..cb2dded2a 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LBPool.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LBPool.java
@@ -16,11 +16,20 @@
 
 package net.floodlightcontroller.loadbalancer;
 
+
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+
+import org.projectfloodlight.openflow.types.U64;
+import org.sdnplatform.sync.internal.util.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
 import net.floodlightcontroller.loadbalancer.LoadBalancer.IPClient;
+import net.floodlightcontroller.statistics.SwitchPortBandwidth;
 
 /**
  * Data structure for Load Balancer based on
@@ -32,43 +41,76 @@ import net.floodlightcontroller.loadbalancer.LoadBalancer.IPClient;
 
 @JsonSerialize(using=LBPoolSerializer.class)
 public class LBPool {
-    protected String id;
-    protected String name;
-    protected String tenantId;
-    protected String netId;
-    protected short lbMethod;
-    protected byte protocol;
-    protected ArrayList<String> members;
-    protected ArrayList<String> monitors;
-    protected short adminState;
-    protected short status;
-    
-    protected String vipId;
-    
-    protected int previousMemberIndex;
-    
-    public LBPool() {
-        id = String.valueOf((int) (Math.random()*10000));
-        name = null;
-        tenantId = null;
-        netId = null;
-        lbMethod = 0;
-        protocol = 0;
-        members = new ArrayList<String>();
-        monitors = new ArrayList<String>();
-        adminState = 0;
-        status = 0;
-        previousMemberIndex = -1;
-    }
-    
-    public String pickMember(IPClient client) {
-        // simple round robin for now; add different lbmethod later
-        if (members.size() > 0) {
-            previousMemberIndex = (previousMemberIndex + 1) % members.size();
-            return members.get(previousMemberIndex);
-        } else {
-            return null;
-        }
-    }
+	protected static Logger log = LoggerFactory.getLogger(LBPool.class);
+	protected String id;
+	protected String name;
+	protected String tenantId;
+	protected String netId;
+	protected short lbMethod;
+	protected byte protocol;
+	protected ArrayList<String> members;
+	protected ArrayList<String> monitors;
+	protected short adminState;
+	protected short status;
+	protected final static short STATISTICS = 2;
+	protected final static short ROUND_ROBIN = 1;
+
+	protected String vipId;
+
+	protected int previousMemberIndex;
+
+	public LBPool() {
+		id = String.valueOf((int) (Math.random()*10000));
+		name = null;
+		tenantId = null;
+		netId = null;
+		lbMethod = 0;
+		protocol = 0;
+		members = new ArrayList<String>();
+		monitors = new ArrayList<String>();
+		adminState = 0;
+		status = 0;
+		previousMemberIndex = -1;
+	}
+
+	public String pickMember(IPClient client, HashMap<String,U64> membersbandwidth) {
+
+		// Get the members that belong to this pool and the statistics for them
+		if(members.size() > 0){
+			if (lbMethod == STATISTICS && membersbandwidth.values() !=null) {	
+				ArrayList<String> memb = new ArrayList<String>();
+				for(String memberId: membersbandwidth.keySet()){
+					for(int i=0;i<members.size();i++){
+						if(members.get(i).equals(memberId)){
+							memb.add(memberId);					
+
+							log.info("MEMBER OF THIS POOL: {}", memberId);
+							log.info("RecievedX: {}",membersbandwidth.get(memberId).getBigInteger());
+
+						}
+					}
+				}
+				// return the member which has the minimum bandwidth usage, out of this pool members
+				if(!memb.isEmpty()){
+					ArrayList<U64> vals = new ArrayList<U64>();
+
+					for(int j=0;j<memb.size();j++){
+						vals.add(membersbandwidth.get(memb.get(j)));
+					}
+					log.info("Members {}", memb);
+					log.info("VALS {}", vals);
+					log.info("MEMBER PICKED {}", memb.get(vals.indexOf(Collections.min(vals))));
+
+					return memb.get(vals.indexOf(Collections.min(vals)));
+				}
+				// simple round robin
+			} else if(lbMethod == ROUND_ROBIN || lbMethod == 0){
+				log.info("ROUND_ROBIN");
 
+				previousMemberIndex = (previousMemberIndex + 1) % members.size();
+				return members.get(previousMemberIndex);
+			}
+		}
+		return null;
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
index de0dbc10c..a017a831b 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.match.Match;
@@ -74,6 +75,8 @@ import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Path;
 import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
+import net.floodlightcontroller.statistics.IStatisticsService;
+import net.floodlightcontroller.statistics.SwitchPortBandwidth;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.util.FlowModUtils;
 import net.floodlightcontroller.util.OFMessageUtils;
@@ -93,758 +96,804 @@ import net.floodlightcontroller.util.OFMessageUtils;
  * @edited Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com
  */
 public class LoadBalancer implements IFloodlightModule,
-    ILoadBalancerService, IOFMessageListener {
-
-    protected static Logger log = LoggerFactory.getLogger(LoadBalancer.class);
-
-    // Our dependencies
-    protected IFloodlightProviderService floodlightProviderService;
-    protected IRestApiService restApiService;
-    
-    protected IDebugCounterService debugCounterService;
-    private IDebugCounter counterPacketOut;
-    protected IDeviceService deviceManagerService;
-    protected IRoutingService routingEngineService;
-    protected ITopologyService topologyService;
-    protected IStaticEntryPusherService sfpService;
-    protected IOFSwitchService switchService;
-    
-    protected HashMap<String, LBVip> vips;
-    protected HashMap<String, LBPool> pools;
-    protected HashMap<String, LBMember> members;
-    protected HashMap<Integer, String> vipIpToId;
-    protected HashMap<Integer, MacAddress> vipIpToMac;
-    protected HashMap<Integer, String> memberIpToId;
-    protected HashMap<IPClient, LBMember> clientToMember;
-    
-    //Copied from Forwarding with message damper routine for pushing proxy Arp 
-    protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // ms. 
-    protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms 
-    protected static String LB_ETHER_TYPE = "0x800";
-    protected static int LB_PRIORITY = 32768;
-    
-    // Comparator for sorting by SwitchCluster
-    public Comparator<SwitchPort> clusterIdComparator =
-            new Comparator<SwitchPort>() {
-                @Override
-                public int compare(SwitchPort d1, SwitchPort d2) {
-                    DatapathId d1ClusterId = topologyService.getClusterId(d1.getNodeId());
-                    DatapathId d2ClusterId = topologyService.getClusterId(d2.getNodeId());
-                    return d1ClusterId.compareTo(d2ClusterId);
-                }
-            };
-
-    // data structure for storing connected
-    public class IPClient {
-        IPv4Address ipAddress;
-        IpProtocol nw_proto;
-        TransportPort srcPort; // tcp/udp src port. icmp type (OFMatch convention)
-        TransportPort targetPort; // tcp/udp dst port, icmp code (OFMatch convention)
-        
-        public IPClient() {
-            ipAddress = IPv4Address.NONE;
-            nw_proto = IpProtocol.NONE;
-            srcPort = TransportPort.NONE;
-            targetPort = TransportPort.NONE;
-        }
-    }
-    
-    @Override
-    public String getName() {
-        return "loadbalancer";
-    }
-
-    @Override
-    public boolean isCallbackOrderingPrereq(OFType type, String name) {
-        return (type.equals(OFType.PACKET_IN) && 
-                (name.equals("topology") || 
-                 name.equals("devicemanager") ||
-                 name.equals("virtualizer")));
-    }
-
-    @Override
-    public boolean isCallbackOrderingPostreq(OFType type, String name) {
-        return (type.equals(OFType.PACKET_IN) && name.equals("forwarding"));
-   }
-
-    @Override
-    public net.floodlightcontroller.core.IListener.Command
-            receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-        switch (msg.getType()) {
-            case PACKET_IN:
-                return processPacketIn(sw, (OFPacketIn)msg, cntx);
-            default:
-                break;
-        }
-        log.warn("Received unexpected message {}", msg);
-        return Command.CONTINUE;
-    }
-
-    private net.floodlightcontroller.core.IListener.Command processPacketIn(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
-        
-        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-        IPacket pkt = eth.getPayload(); 
- 
-        if (eth.isBroadcast() || eth.isMulticast()) {
-            // handle ARP for VIP
-            if (pkt instanceof ARP) {
-                // retrieve arp to determine target IP address                                                       
-                ARP arpRequest = (ARP) eth.getPayload();
-
-                IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress();
-
-                if (vipIpToId.containsKey(targetProtocolAddress.getInt())) {
-                    String vipId = vipIpToId.get(targetProtocolAddress.getInt());
-                    vipProxyArpReply(sw, pi, cntx, vipId);
-                    return Command.STOP;
-                }
-            }
-        } else {
-            // currently only load balance IPv4 packets - no-op for other traffic 
-            if (pkt instanceof IPv4) {
-                IPv4 ip_pkt = (IPv4) pkt;
-                
-                // If match Vip and port, check pool and choose member
-                int destIpAddress = ip_pkt.getDestinationAddress().getInt();
-                
-                if (vipIpToId.containsKey(destIpAddress)){
-                    IPClient client = new IPClient();
-                    client.ipAddress = ip_pkt.getSourceAddress();
-                    client.nw_proto = ip_pkt.getProtocol();
-                    if (ip_pkt.getPayload() instanceof TCP) {
-                        TCP tcp_pkt = (TCP) ip_pkt.getPayload();
-                        client.srcPort = tcp_pkt.getSourcePort();
-                        client.targetPort = tcp_pkt.getDestinationPort();
-                    }
-                    if (ip_pkt.getPayload() instanceof UDP) {
-                        UDP udp_pkt = (UDP) ip_pkt.getPayload();
-                        client.srcPort = udp_pkt.getSourcePort();
-                        client.targetPort = udp_pkt.getDestinationPort();
-                    }
-                    if (ip_pkt.getPayload() instanceof ICMP) {
-                        client.srcPort = TransportPort.of(8); 
-                        client.targetPort = TransportPort.of(0); 
-                    }
-                    
-                    LBVip vip = vips.get(vipIpToId.get(destIpAddress));
-                    if (vip == null)			// fix dereference violations           
-                    	return Command.CONTINUE;
-                    LBPool pool = pools.get(vip.pickPool(client));
-                    if (pool == null)			// fix dereference violations
-                    	return Command.CONTINUE;
-                    LBMember member = members.get(pool.pickMember(client));
-                    if(member == null)			//fix dereference violations
-                    	return Command.CONTINUE;
-                    
-                    // for chosen member, check device manager and find and push routes, in both directions                    
-                    pushBidirectionalVipRoutes(sw, pi, cntx, client, member);
-                   
-                    // packet out based on table rule
-                    pushPacket(pkt, sw, pi.getBufferId(), (pi.getVersion().compareTo(OFVersion.OF_12) < 0) ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT), OFPort.TABLE,
-                                cntx, true);
-
-                    return Command.STOP;
-                }
-            }
-        }
-        // bypass non-load-balanced traffic for normal processing (forwarding)
-        return Command.CONTINUE;
-    }
-
-    /**
-     * used to send proxy Arp for load balanced service requests
-     * @param IOFSwitch sw
-     * @param OFPacketIn pi
-     * @param FloodlightContext cntx
-     * @param String vipId
-     */
-    
-    protected void vipProxyArpReply(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, String vipId) {
-        log.debug("vipProxyArpReply");
-            
-        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
-                                                              IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-
-        // retrieve original arp to determine host configured gw IP address                                          
-        if (! (eth.getPayload() instanceof ARP))
-            return;
-        ARP arpRequest = (ARP) eth.getPayload();
-        
-        // have to do proxy arp reply since at this point we cannot determine the requesting application type
-        
-        // generate proxy ARP reply
-        IPacket arpReply = new Ethernet()
-            .setSourceMACAddress(vips.get(vipId).proxyMac)
-            .setDestinationMACAddress(eth.getSourceMACAddress())
-            .setEtherType(EthType.ARP)
-            .setVlanID(eth.getVlanID())
-            .setPriorityCode(eth.getPriorityCode())
-            .setPayload(
-                new ARP()
-                .setHardwareType(ARP.HW_TYPE_ETHERNET)
-                .setProtocolType(ARP.PROTO_TYPE_IP)
-                .setHardwareAddressLength((byte) 6)
-                .setProtocolAddressLength((byte) 4)
-                .setOpCode(ARP.OP_REPLY)
-                .setSenderHardwareAddress(vips.get(vipId).proxyMac)
-                .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
-                .setTargetHardwareAddress(eth.getSourceMACAddress())
-                .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress()));
-                
-        // push ARP reply out
-        pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ANY, (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)), cntx, true);
-        log.debug("proxy ARP reply pushed as {}", IPv4.fromIPv4Address(vips.get(vipId).address));
-        
-        return;
-    }
-
-    /**
-     * used to push any packet - borrowed routine from Forwarding
-     * 
-     * @param OFPacketIn pi
-     * @param IOFSwitch sw
-     * @param int bufferId
-     * @param short inPort
-     * @param short outPort
-     * @param FloodlightContext cntx
-     * @param boolean flush
-     */    
-    public void pushPacket(IPacket packet, 
-                           IOFSwitch sw,
-                           OFBufferId bufferId,
-                           OFPort inPort,
-                           OFPort outPort, 
-                           FloodlightContext cntx,
-                           boolean flush) {
-        if (log.isTraceEnabled()) {
-            log.trace("PacketOut srcSwitch={} inPort={} outPort={}", 
-                      new Object[] {sw, inPort, outPort});
-        }
-
-        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-
-        // set actions
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(sw.getOFFactory().actions().buildOutput().setPort(outPort).setMaxLen(Integer.MAX_VALUE).build());
-
-        pob.setActions(actions);
-        
-        // set buffer_id, in_port
-        pob.setBufferId(bufferId);
-        pob.setInPort(inPort);
-
-        // set data - only if buffer_id == -1
-        if (pob.getBufferId() == OFBufferId.NO_BUFFER) {
-            if (packet == null) {
-                log.error("BufferId is not set and packet data is null. " +
-                          "Cannot send packetOut. " +
-                        "srcSwitch={} inPort={} outPort={}",
-                        new Object[] {sw, inPort, outPort});
-                return;
-            }
-            byte[] packetData = packet.serialize();
-            pob.setData(packetData);
-        }
-
-        counterPacketOut.increment();
-        sw.write(pob.build());
-    }
-
-    /**
-     * used to find and push in-bound and out-bound routes using StaticFlowEntryPusher
-     * @param IOFSwitch sw
-     * @param OFPacketIn pi
-     * @param FloodlightContext cntx
-     * @param IPClient client
-     * @param LBMember member
-     */
-    protected void pushBidirectionalVipRoutes(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, IPClient client, LBMember member) {
-        
-        // borrowed code from Forwarding to retrieve src and dst device entities
-        // Check if we have the location of the destination
-        IDevice srcDevice = null;
-        IDevice dstDevice = null;
-        
-        // retrieve all known devices
-        Collection<? extends IDevice> allDevices = deviceManagerService.getAllDevices();
-        
-        for (IDevice d : allDevices) {
-            for (int j = 0; j < d.getIPv4Addresses().length; j++) {
-                    if (srcDevice == null && client.ipAddress.equals(d.getIPv4Addresses()[j]))
-                        srcDevice = d;
-                    if (dstDevice == null && member.address == d.getIPv4Addresses()[j].getInt()) {
-                        dstDevice = d;
-                        member.macString = dstDevice.getMACAddressString();
-                    }
-                    if (srcDevice != null && dstDevice != null)
-                        break;
-            }
-        }  
-        
-        // srcDevice and/or dstDevice is null, no route can be pushed
-        if (srcDevice == null || dstDevice == null) return;
-        
-        DatapathId srcIsland = topologyService.getClusterId(sw.getId());
-
-        if (srcIsland == null) {
-            log.debug("No openflow island found for source {}/{}", 
-                      sw.getId().toString(), pi.getInPort());
-            return;
-        }
-        
-        // Validate that we have a destination known on the same island
-        // Validate that the source and destination are not on the same switchport
-        boolean on_same_island = false;
-        boolean on_same_if = false;
-        for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
-            DatapathId dstSwDpid = dstDap.getNodeId();
-            DatapathId dstIsland = topologyService.getClusterId(dstSwDpid);
-            if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
-                on_same_island = true;
-                if ((sw.getId().equals(dstSwDpid)) && OFMessageUtils.getInPort(pi).equals(dstDap.getPortId())) {
-                    on_same_if = true;
-                }
-                break;
-            }
-        }
-        
-        if (!on_same_island) {
-            // Flood since we don't know the dst device
-            if (log.isTraceEnabled()) {
-                log.trace("No first hop island found for destination " + 
-                        "device {}, Action = flooding", dstDevice);
-            }
-            return;
-        }            
-        
-        if (on_same_if) {
-            if (log.isTraceEnabled()) {
-                log.trace("Both source and destination are on the same " + 
-                        "switch/port {}/{}, Action = NOP", 
-                        sw.toString(), pi.getInPort());
-            }
-            return;
-        }
-        
-        // Install all the routes where both src and dst have attachment
-        // points.  Since the lists are stored in sorted order we can 
-        // traverse the attachment points in O(m+n) time
-        SwitchPort[] srcDaps = srcDevice.getAttachmentPoints();
-        Arrays.sort(srcDaps, clusterIdComparator);
-        SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
-        Arrays.sort(dstDaps, clusterIdComparator);
-        
-        int iSrcDaps = 0, iDstDaps = 0;
-
-        // following Forwarding's same routing routine, retrieve both in-bound and out-bound routes for
-        // all clusters.
-        while ((iSrcDaps < srcDaps.length) && (iDstDaps < dstDaps.length)) {
-            SwitchPort srcDap = srcDaps[iSrcDaps];
-            SwitchPort dstDap = dstDaps[iDstDaps];
-            DatapathId srcCluster = 
-                    topologyService.getClusterId(srcDap.getNodeId());
-            DatapathId dstCluster = 
-                    topologyService.getClusterId(dstDap.getNodeId());
-
-            int srcVsDest = srcCluster.compareTo(dstCluster);
-            if (srcVsDest == 0) {
-                if (!srcDap.equals(dstDap) && 
-                        (srcCluster != null) && 
-                        (dstCluster != null)) {
-                    Path routeIn = 
-                            routingEngineService.getPath(srcDap.getNodeId(),
-                                                   srcDap.getPortId(),
-                                                   dstDap.getNodeId(),
-                                                   dstDap.getPortId());
-                    Path routeOut = 
-                            routingEngineService.getPath(dstDap.getNodeId(),
-                                                   dstDap.getPortId(),
-                                                   srcDap.getNodeId(),
-                                                   srcDap.getPortId());
-
-                    // use static flow entry pusher to push flow mod along in and out path
-                    // in: match src client (ip, port), rewrite dest from vip ip/port to member ip/port, forward
-                    // out: match dest client (ip, port), rewrite src from member ip/port to vip ip/port, forward
-                    
-                    if (! routeIn.getPath().isEmpty()) {
-                        pushStaticVipRoute(true, routeIn, client, member, sw);
-                    }
-                    
-                    if (! routeOut.getPath().isEmpty()) {
-                        pushStaticVipRoute(false, routeOut, client, member, sw);
-                    }
-
-                }
-                iSrcDaps++;
-                iDstDaps++;
-            } else if (srcVsDest < 0) {
-                iSrcDaps++;
-            } else {
-                iDstDaps++;
-            }
-        }
-        return;
-    }
-    
-    /**
-     * used to push given route using static flow entry pusher
-     * @param boolean inBound
-     * @param Path route
-     * @param IPClient client
-     * @param LBMember member
-     * @param long pinSwitch
-     */
-    public void pushStaticVipRoute(boolean inBound, Path route, IPClient client, LBMember member, IOFSwitch pinSwitch) {
-        List<NodePortTuple> path = route.getPath();
-        if (path.size() > 0) {
-           for (int i = 0; i < path.size(); i+=2) {
-               DatapathId sw = path.get(i).getNodeId();
-               String entryName;
-               Match.Builder mb = pinSwitch.getOFFactory().buildMatch();
-               ArrayList<OFAction> actions = new ArrayList<OFAction>();
-               
-               OFFlowMod.Builder fmb = pinSwitch.getOFFactory().buildFlowAdd();
-
-               fmb.setIdleTimeout(FlowModUtils.INFINITE_TIMEOUT);
-               fmb.setHardTimeout(FlowModUtils.INFINITE_TIMEOUT);
-               fmb.setBufferId(OFBufferId.NO_BUFFER);
-               fmb.setOutPort(OFPort.ANY);
-               fmb.setCookie(U64.of(0));  
-               fmb.setPriority(FlowModUtils.PRIORITY_MAX);
-               
-               
-               if (inBound) {
-                   entryName = "inbound-vip-"+ member.vipId+"-client-"+client.ipAddress
-                		   +"-srcport-"+client.srcPort+"-dstport-"+client.targetPort
-                           +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
-                   mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
-                   .setExact(MatchField.IP_PROTO, client.nw_proto)
-                   .setExact(MatchField.IPV4_SRC, client.ipAddress)
-                   .setExact(MatchField.IN_PORT, path.get(i).getPortId());
-                   if (client.nw_proto.equals(IpProtocol.TCP)) {
-                	   mb.setExact(MatchField.TCP_SRC, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.UDP)) {
-                	   mb.setExact(MatchField.UDP_SRC, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.SCTP)) {
-                	   mb.setExact(MatchField.SCTP_SRC, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.ICMP)) {
-                	   /* no-op */
-                   } else {
-                	   log.error("Unknown IpProtocol {} detected during inbound static VIP route push.", client.nw_proto);
-                   }
-                   
-
-                   if (sw.equals(pinSwitch.getId())) {
-                       if (pinSwitch.getOFFactory().getVersion().compareTo(OFVersion.OF_12) < 0) { 
-                    	   actions.add(pinSwitch.getOFFactory().actions().setDlDst(MacAddress.of(member.macString)));
-                    	   actions.add(pinSwitch.getOFFactory().actions().setNwDst(IPv4Address.of(member.address)));
-                    	   actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                       } else { // OXM introduced in OF1.2
-                    	   actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ethDst(MacAddress.of(member.macString))));
-                    	   actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ipv4Dst(IPv4Address.of(member.address))));
-                    	   actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                       }
-                   } else {
-                	   //fix concurrency errors
-                	   try{
-                		   actions.add(switchService.getSwitch(path.get(i+1).getNodeId()).getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                	   }
-                	   catch(NullPointerException e){
-                		   log.error("Fail to install loadbalancer flow rules to offline switch {}.", path.get(i+1).getNodeId());
-                	   }
-                   }
-               } else {
-                   entryName = "outbound-vip-"+ member.vipId+"-client-"+client.ipAddress
-                		   +"-srcport-"+client.srcPort+"-dstport-"+client.targetPort
-                           +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
-                   mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
-                   .setExact(MatchField.IP_PROTO, client.nw_proto)
-                   .setExact(MatchField.IPV4_DST, client.ipAddress)
-                   .setExact(MatchField.IN_PORT, path.get(i).getPortId());
-                   if (client.nw_proto.equals(IpProtocol.TCP)) {
-                	   mb.setExact(MatchField.TCP_DST, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.UDP)) {
-                	   mb.setExact(MatchField.UDP_DST, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.SCTP)) {
-                	   mb.setExact(MatchField.SCTP_DST, client.srcPort);
-                   } else if (client.nw_proto.equals(IpProtocol.ICMP)) {
-                	   /* no-op */
-                   } else {
-                	   log.error("Unknown IpProtocol {} detected during outbound static VIP route push.", client.nw_proto);
-                   }
-                   
-                   if (sw.equals(pinSwitch.getId())) {
-                       if (pinSwitch.getOFFactory().getVersion().compareTo(OFVersion.OF_12) < 0) { 
-                    	   actions.add(pinSwitch.getOFFactory().actions().setDlSrc(vips.get(member.vipId).proxyMac));
-                    	   actions.add(pinSwitch.getOFFactory().actions().setNwSrc(IPv4Address.of(vips.get(member.vipId).address)));
-                    	   actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                       } else { // OXM introduced in OF1.2
-                    	   actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ethSrc(vips.get(member.vipId).proxyMac)));
-                    	   actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ipv4Src(IPv4Address.of(vips.get(member.vipId).address))));
-                    	   actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                       }
-                   } else {
-                	   //fix concurrency errors
-                	   try{
-                		   actions.add(switchService.getSwitch(path.get(i+1).getNodeId()).getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
-                	   }
-                	   catch(NullPointerException e){
-                		   log.error("Fail to install loadbalancer flow rules to offline switches {}.", path.get(i+1).getNodeId());
-                	   }
-                   }
-                   
-               }
-               
-               
-               fmb.setActions(actions);
-               fmb.setPriority(U16.t(LB_PRIORITY));
-               fmb.setMatch(mb.build());
-               sfpService.addFlow(entryName, fmb.build(), sw);
-           }
-        }
-        return;
-    }
-
-    
-    @Override
-    public Collection<LBVip> listVips() {
-        return vips.values();
-    }
-
-    @Override
-    public Collection<LBVip> listVip(String vipId) {
-        Collection<LBVip> result = new HashSet<LBVip>();
-        result.add(vips.get(vipId));
-        return result;
-    }
-
-    @Override
-    public LBVip createVip(LBVip vip) {
-        if (vip == null)
-            vip = new LBVip();
-        
-        vips.put(vip.id, vip);
-        vipIpToId.put(vip.address, vip.id);
-        vipIpToMac.put(vip.address, vip.proxyMac);
-        
-        return vip;
-    }
-
-    @Override
-    public LBVip updateVip(LBVip vip) {
-        vips.put(vip.id, vip);
-        return vip;
-    }
-
-    @Override
-    public int removeVip(String vipId) {
-        if(vips.containsKey(vipId)){
-            vips.remove(vipId);
-            return 0;
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public Collection<LBPool> listPools() {
-        return pools.values();
-    }
-
-    @Override
-    public Collection<LBPool> listPool(String poolId) {
-        Collection<LBPool> result = new HashSet<LBPool>();
-        result.add(pools.get(poolId));
-        return result;
-    }
-
-    @Override
-    public LBPool createPool(LBPool pool) {
-        if (pool == null)
-            pool = new LBPool();
-        
-        pools.put(pool.id, pool);
-        if (pool.vipId != null && vips.containsKey(pool.vipId))
-            vips.get(pool.vipId).pools.add(pool.id);
-        else {
-            log.error("specified vip-id must exist");
-            pool.vipId = null;
-            pools.put(pool.id, pool);
-        }
-        return pool;
-    }
-
-    @Override
-    public LBPool updatePool(LBPool pool) {
-        pools.put(pool.id, pool);
-        return null;
-    }
-
-    @Override
-    public int removePool(String poolId) {
-        LBPool pool;
-        if (pools != null) {
-            pool = pools.get(poolId);
-            if (pool == null)	// fix dereference violations
-            	return -1;
-            if (pool.vipId != null && vips.containsKey(pool.vipId))
-                vips.get(pool.vipId).pools.remove(poolId);
-            pools.remove(poolId);
-            return 0;
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public Collection<LBMember> listMembers() {
-        return members.values();
-    }
-
-    @Override
-    public Collection<LBMember> listMember(String memberId) {
-        Collection<LBMember> result = new HashSet<LBMember>();
-        result.add(members.get(memberId));
-        return result;
-        }
-
-    @Override
-    public Collection<LBMember> listMembersByPool(String poolId) {
-        Collection<LBMember> result = new HashSet<LBMember>();
-        
-        if(pools.containsKey(poolId)) {
-            ArrayList<String> memberIds = pools.get(poolId).members;
-            for (int i = 0; i<memberIds.size(); i++)
-                result.add(members.get(memberIds.get(i)));
-        }
-        return result;
-    }
-    
-    @Override
-    public LBMember createMember(LBMember member) {
-        if (member == null)
-            member = new LBMember();
-
-        members.put(member.id, member);
-        memberIpToId.put(member.address, member.id);
-
-        if (member.poolId != null && pools.get(member.poolId) != null) {
-            member.vipId = pools.get(member.poolId).vipId;
-            if (!pools.get(member.poolId).members.contains(member.id))
-                pools.get(member.poolId).members.add(member.id);
-        } else
-            log.error("member must be specified with non-null pool_id");
-        
-        return member;
-    }
-
-    @Override
-    public LBMember updateMember(LBMember member) {
-        members.put(member.id, member);
-        return member;
-    }
-
-    @Override
-    public int removeMember(String memberId) {
-        LBMember member;
-        member = members.get(memberId);
-        
-        if(member != null){
-            if (member.poolId != null && pools.containsKey(member.poolId))
-                pools.get(member.poolId).members.remove(memberId);
-            members.remove(memberId);
-            return 0;
-        } else {
-            return -1;
-        }    
-    }
-
-    @Override
-    public Collection<LBMonitor> listMonitors() {
-        return null;
-    }
-
-    @Override
-    public Collection<LBMonitor> listMonitor(String monitorId) {
-        return null;
-    }
-
-    @Override
-    public LBMonitor createMonitor(LBMonitor monitor) {
-        return null;
-    }
-
-    @Override
-    public LBMonitor updateMonitor(LBMonitor monitor) {
-        return null;
-    }
-
-    @Override
-    public int removeMonitor(String monitorId) {
-        return 0;
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> l = 
-                new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(ILoadBalancerService.class);
-        return l;
-   }
-
-    @Override
-    public Map<Class<? extends IFloodlightService>, IFloodlightService>
-            getServiceImpls() {
-        Map<Class<? extends IFloodlightService>, IFloodlightService> m = 
-                new HashMap<Class<? extends IFloodlightService>,
-                    IFloodlightService>();
-        m.put(ILoadBalancerService.class, this);
-        return m;
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
-        Collection<Class<? extends IFloodlightService>> l = 
-                new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(IFloodlightProviderService.class);
-        l.add(IRestApiService.class);
-        l.add(IOFSwitchService.class);
-        l.add(IDeviceService.class);
-        l.add(IDebugCounterService.class);
-        l.add(ITopologyService.class);
-        l.add(IRoutingService.class);
-        l.add(IStaticEntryPusherService.class);
-
-        return l;
-    }
-
-    @Override
-    public void init(FloodlightModuleContext context)
-                                                 throws FloodlightModuleException {
-        floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
-        restApiService = context.getServiceImpl(IRestApiService.class);
-        debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-        deviceManagerService = context.getServiceImpl(IDeviceService.class);
-        routingEngineService = context.getServiceImpl(IRoutingService.class);
-        topologyService = context.getServiceImpl(ITopologyService.class);
-        sfpService = context.getServiceImpl(IStaticEntryPusherService.class);
-        switchService = context.getServiceImpl(IOFSwitchService.class);
-        
-        vips = new HashMap<String, LBVip>();
-        pools = new HashMap<String, LBPool>();
-        members = new HashMap<String, LBMember>();
-        vipIpToId = new HashMap<Integer, String>();
-        vipIpToMac = new HashMap<Integer, MacAddress>();
-        memberIpToId = new HashMap<Integer, String>();
-    }
-
-    @Override
-    public void startUp(FloodlightModuleContext context) {
-        floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
-        restApiService.addRestletRoutable(new LoadBalancerWebRoutable());
-        debugCounterService.registerModule(this.getName());
-        counterPacketOut = debugCounterService.registerCounter(this.getName(), "packet-outs-written", "Packet outs written by the LoadBalancer", MetaData.WARN);
-    }
+ILoadBalancerService, IOFMessageListener {
+
+	protected static Logger log = LoggerFactory.getLogger(LoadBalancer.class);
+
+	// Our dependencies
+	protected IFloodlightProviderService floodlightProviderService;
+	protected IRestApiService restApiService;
+
+	protected IDebugCounterService debugCounterService;
+	private IDebugCounter counterPacketOut;
+	protected IDeviceService deviceManagerService;
+	protected IRoutingService routingEngineService;
+	protected ITopologyService topologyService;
+	protected IStaticEntryPusherService sfpService;
+	protected IOFSwitchService switchService;
+	protected IStatisticsService statisticsService;
+
+	protected HashMap<String, LBVip> vips;
+	protected HashMap<String, LBPool> pools;
+	protected HashMap<String, LBMember> members;
+	protected HashMap<Integer, String> vipIpToId;
+	protected HashMap<Integer, MacAddress> vipIpToMac;
+	protected HashMap<Integer, String> memberIpToId;
+	protected HashMap<IPClient, LBMember> clientToMember;
+	protected HashMap<IDevice, String> deviceToMemberId;
+
+	//Copied from Forwarding with message damper routine for pushing proxy Arp 
+	protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // ms. 
+	protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms 
+	protected static String LB_ETHER_TYPE = "0x800";
+	protected static int LB_PRIORITY = 32768;
+
+	// Comparator for sorting by SwitchCluster
+	public Comparator<SwitchPort> clusterIdComparator =
+			new Comparator<SwitchPort>() {
+		@Override
+		public int compare(SwitchPort d1, SwitchPort d2) {
+			DatapathId d1ClusterId = topologyService.getClusterId(d1.getNodeId());
+			DatapathId d2ClusterId = topologyService.getClusterId(d2.getNodeId());
+			return d1ClusterId.compareTo(d2ClusterId);
+		}
+	};
+
+	// data structure for storing connected
+	public class IPClient {
+		IPv4Address ipAddress;
+		IpProtocol nw_proto;
+		TransportPort srcPort; // tcp/udp src port. icmp type (OFMatch convention)
+		TransportPort targetPort; // tcp/udp dst port, icmp code (OFMatch convention)
+
+		public IPClient() {
+			ipAddress = IPv4Address.NONE;
+			nw_proto = IpProtocol.NONE;
+			srcPort = TransportPort.NONE;
+			targetPort = TransportPort.NONE;
+		}
+	}
+
+	@Override
+	public String getName() {
+		return "loadbalancer";
+	}
+
+	@Override
+	public boolean isCallbackOrderingPrereq(OFType type, String name) {
+		return (type.equals(OFType.PACKET_IN) && 
+				(name.equals("topology") || 
+						name.equals("devicemanager") ||
+						name.equals("virtualizer")));
+	}
+
+	@Override
+	public boolean isCallbackOrderingPostreq(OFType type, String name) {
+		return (type.equals(OFType.PACKET_IN) && name.equals("forwarding"));
+	}
+
+	@Override
+	public net.floodlightcontroller.core.IListener.Command
+	receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+		switch (msg.getType()) {
+		case PACKET_IN:
+			return processPacketIn(sw, (OFPacketIn)msg, cntx);
+		default:
+			break;
+		}
+		log.warn("Received unexpected message {}", msg);
+		return Command.CONTINUE;
+	}
+
+	private net.floodlightcontroller.core.IListener.Command processPacketIn(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
+
+		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+		IPacket pkt = eth.getPayload(); 
+
+		if (eth.isBroadcast() || eth.isMulticast()) {
+			// handle ARP for VIP
+			if (pkt instanceof ARP) {
+				// retrieve arp to determine target IP address                                                       
+				ARP arpRequest = (ARP) eth.getPayload();
+
+				IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress();
+
+				if (vipIpToId.containsKey(targetProtocolAddress.getInt())) {
+					String vipId = vipIpToId.get(targetProtocolAddress.getInt());
+					vipProxyArpReply(sw, pi, cntx, vipId);
+					return Command.STOP;
+				}
+			}
+		} else {
+			// currently only load balance IPv4 packets - no-op for other traffic 
+			if (pkt instanceof IPv4) {
+				IPv4 ip_pkt = (IPv4) pkt;
+
+				// If match Vip and port, check pool and choose member
+				int destIpAddress = ip_pkt.getDestinationAddress().getInt();
+
+				if (vipIpToId.containsKey(destIpAddress)){
+					
+					// Switch statistics collection for picking a member
+					HashMap<String, U64> memberPortBandwidth = collectSwitchStatistics();
+
+					IPClient client = new IPClient();
+					client.ipAddress = ip_pkt.getSourceAddress();
+					client.nw_proto = ip_pkt.getProtocol();
+					if (ip_pkt.getPayload() instanceof TCP) {
+						TCP tcp_pkt = (TCP) ip_pkt.getPayload();
+						client.srcPort = tcp_pkt.getSourcePort();
+						client.targetPort = tcp_pkt.getDestinationPort();
+					}
+					if (ip_pkt.getPayload() instanceof UDP) {
+						UDP udp_pkt = (UDP) ip_pkt.getPayload();
+						client.srcPort = udp_pkt.getSourcePort();
+						client.targetPort = udp_pkt.getDestinationPort();
+					}
+					if (ip_pkt.getPayload() instanceof ICMP) {
+						client.srcPort = TransportPort.of(8); 
+						client.targetPort = TransportPort.of(0); 
+					}
+
+
+					LBVip vip = vips.get(vipIpToId.get(destIpAddress));
+					if (vip == null)			// fix dereference violations           
+						return Command.CONTINUE;
+					LBPool pool = pools.get(vip.pickPool(client));
+					if (pool == null)			// fix dereference violations
+						return Command.CONTINUE;
+					LBMember member = members.get(pool.pickMember(client,memberPortBandwidth));
+					if(member == null)			//fix dereference violations
+						return Command.CONTINUE;
+
+					// for chosen member, check device manager and find and push routes, in both directions                    
+					pushBidirectionalVipRoutes(sw, pi, cntx, client, member);
+
+					// packet out based on table rule
+					pushPacket(pkt, sw, pi.getBufferId(), (pi.getVersion().compareTo(OFVersion.OF_12) < 0) ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT), OFPort.TABLE,
+							cntx, true);
+
+					return Command.STOP;
+				}
+			}
+		}
+		// bypass non-load-balanced traffic for normal processing (forwarding)
+		return Command.CONTINUE;
+	}
+	
+	/**
+	 * used to collect statistics from members switch port
+	 * @return HashMap<String, U64> portBandwidth <memberId,bitsPerSecond RX> of port connected to member
+	 */
+	public HashMap<String, U64> collectSwitchStatistics(){
+		HashMap<String,U64> portBandwidth = new HashMap<String, U64>();
+
+		// retrieve all known devices to know which ones are attached to the members
+		Collection<? extends IDevice> allDevices = deviceManagerService.getAllDevices();
+
+		for (IDevice d : allDevices) {
+			for (int j = 0; j < d.getIPv4Addresses().length; j++) {
+				if(members != null){
+					for(LBMember member: members.values()){
+						if (member.address == d.getIPv4Addresses()[j].getInt())
+							deviceToMemberId.put(d, member.id);
+					}
+				}
+			}
+		}
+
+		// collect statistics of the switch ports attached to the members
+		if(deviceToMemberId != null){
+			for(IDevice membersDevice: deviceToMemberId.keySet()){
+				String memberId = deviceToMemberId.get(membersDevice);
+				for(SwitchPort dstDap: membersDevice.getAttachmentPoints()){
+					portBandwidth.put(memberId, statisticsService.getBandwidthConsumption(dstDap.getNodeId(), dstDap.getPortId()).getBitsPerSecondRx());
+				}
+			}
+		}
+		return portBandwidth;
+	}
+
+	/**
+	 * used to send proxy Arp for load balanced service requests
+	 * @param IOFSwitch sw
+	 * @param OFPacketIn pi
+	 * @param FloodlightContext cntx
+	 * @param String vipId
+	 */
+
+	protected void vipProxyArpReply(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, String vipId) {
+		log.debug("vipProxyArpReply");
+
+		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
+				IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+		// retrieve original arp to determine host configured gw IP address                                          
+		if (! (eth.getPayload() instanceof ARP))
+			return;
+		ARP arpRequest = (ARP) eth.getPayload();
+
+		// have to do proxy arp reply since at this point we cannot determine the requesting application type
+
+		// generate proxy ARP reply
+		IPacket arpReply = new Ethernet()
+				.setSourceMACAddress(vips.get(vipId).proxyMac)
+				.setDestinationMACAddress(eth.getSourceMACAddress())
+				.setEtherType(EthType.ARP)
+				.setVlanID(eth.getVlanID())
+				.setPriorityCode(eth.getPriorityCode())
+				.setPayload(
+						new ARP()
+						.setHardwareType(ARP.HW_TYPE_ETHERNET)
+						.setProtocolType(ARP.PROTO_TYPE_IP)
+						.setHardwareAddressLength((byte) 6)
+						.setProtocolAddressLength((byte) 4)
+						.setOpCode(ARP.OP_REPLY)
+						.setSenderHardwareAddress(vips.get(vipId).proxyMac)
+						.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+						.setTargetHardwareAddress(eth.getSourceMACAddress())
+						.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress()));
+
+		// push ARP reply out
+		pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ANY, (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)), cntx, true);
+		log.debug("proxy ARP reply pushed as {}", IPv4.fromIPv4Address(vips.get(vipId).address));
+
+		return;
+	}
+
+	/**
+	 * used to push any packet - borrowed routine from Forwarding
+	 * 
+	 * @param OFPacketIn pi
+	 * @param IOFSwitch sw
+	 * @param int bufferId
+	 * @param short inPort
+	 * @param short outPort
+	 * @param FloodlightContext cntx
+	 * @param boolean flush
+	 */    
+	public void pushPacket(IPacket packet, 
+			IOFSwitch sw,
+			OFBufferId bufferId,
+			OFPort inPort,
+			OFPort outPort, 
+			FloodlightContext cntx,
+			boolean flush) {
+		if (log.isTraceEnabled()) {
+			log.trace("PacketOut srcSwitch={} inPort={} outPort={}", 
+					new Object[] {sw, inPort, outPort});
+		}
+
+		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+
+		// set actions
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(sw.getOFFactory().actions().buildOutput().setPort(outPort).setMaxLen(Integer.MAX_VALUE).build());
+
+		pob.setActions(actions);
+
+		// set buffer_id, in_port
+		pob.setBufferId(bufferId);
+		pob.setInPort(inPort);
+
+		// set data - only if buffer_id == -1
+		if (pob.getBufferId() == OFBufferId.NO_BUFFER) {
+			if (packet == null) {
+				log.error("BufferId is not set and packet data is null. " +
+						"Cannot send packetOut. " +
+						"srcSwitch={} inPort={} outPort={}",
+						new Object[] {sw, inPort, outPort});
+				return;
+			}
+			byte[] packetData = packet.serialize();
+			pob.setData(packetData);
+		}
+
+		counterPacketOut.increment();
+		sw.write(pob.build());
+	}
+
+	/**
+	 * used to find and push in-bound and out-bound routes using StaticFlowEntryPusher
+	 * @param IOFSwitch sw
+	 * @param OFPacketIn pi
+	 * @param FloodlightContext cntx
+	 * @param IPClient client
+	 * @param LBMember member
+	 */
+	protected void pushBidirectionalVipRoutes(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, IPClient client, LBMember member) {
+
+		// borrowed code from Forwarding to retrieve src and dst device entities
+		// Check if we have the location of the destination
+		IDevice srcDevice = null;
+		IDevice dstDevice = null;
+
+		// retrieve all known devices
+		Collection<? extends IDevice> allDevices = deviceManagerService.getAllDevices();
+
+		for (IDevice d : allDevices) {
+			for (int j = 0; j < d.getIPv4Addresses().length; j++) {
+				if (srcDevice == null && client.ipAddress.equals(d.getIPv4Addresses()[j]))
+					srcDevice = d;
+				if (dstDevice == null && member.address == d.getIPv4Addresses()[j].getInt()) {
+					dstDevice = d;
+					member.macString = dstDevice.getMACAddressString();
+				}
+				if (srcDevice != null && dstDevice != null)
+					break;
+			}
+		}  
+
+		// srcDevice and/or dstDevice is null, no route can be pushed
+		if (srcDevice == null || dstDevice == null) return;
+
+		DatapathId srcIsland = topologyService.getClusterId(sw.getId());
+
+		if (srcIsland == null) {
+			log.debug("No openflow island found for source {}/{}", 
+					sw.getId().toString(), pi.getInPort());
+			return;
+		}
+
+		// Validate that we have a destination known on the same island
+		// Validate that the source and destination are not on the same switchport
+		boolean on_same_island = false;
+		boolean on_same_if = false;
+		for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
+			DatapathId dstSwDpid = dstDap.getNodeId();
+			DatapathId dstIsland = topologyService.getClusterId(dstSwDpid);
+			if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
+				on_same_island = true;
+				if ((sw.getId().equals(dstSwDpid)) && OFMessageUtils.getInPort(pi).equals(dstDap.getPortId())) {
+					on_same_if = true;
+				}
+				break;
+			}
+		}
+
+		if (!on_same_island) {
+			// Flood since we don't know the dst device
+			if (log.isTraceEnabled()) {
+				log.trace("No first hop island found for destination " + 
+						"device {}, Action = flooding", dstDevice);
+			}
+			return;
+		}            
+
+		if (on_same_if) {
+			if (log.isTraceEnabled()) {
+				log.trace("Both source and destination are on the same " + 
+						"switch/port {}/{}, Action = NOP", 
+						sw.toString(), pi.getInPort());
+			}
+			return;
+		}
+
+		// Install all the routes where both src and dst have attachment
+		// points.  Since the lists are stored in sorted order we can 
+		// traverse the attachment points in O(m+n) time
+		SwitchPort[] srcDaps = srcDevice.getAttachmentPoints();
+		Arrays.sort(srcDaps, clusterIdComparator);
+		SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
+		Arrays.sort(dstDaps, clusterIdComparator);
+
+		int iSrcDaps = 0, iDstDaps = 0;
+
+		// following Forwarding's same routing routine, retrieve both in-bound and out-bound routes for
+		// all clusters.
+		while ((iSrcDaps < srcDaps.length) && (iDstDaps < dstDaps.length)) {
+			SwitchPort srcDap = srcDaps[iSrcDaps];
+			SwitchPort dstDap = dstDaps[iDstDaps];
+			DatapathId srcCluster = 
+					topologyService.getClusterId(srcDap.getNodeId());
+			DatapathId dstCluster = 
+					topologyService.getClusterId(dstDap.getNodeId());
+
+			int srcVsDest = srcCluster.compareTo(dstCluster);
+			if (srcVsDest == 0) {
+				if (!srcDap.equals(dstDap) && 
+						(srcCluster != null) && 
+						(dstCluster != null)) {
+					Path routeIn = 
+							routingEngineService.getPath(srcDap.getNodeId(),
+									srcDap.getPortId(),
+									dstDap.getNodeId(),
+									dstDap.getPortId());
+					Path routeOut = 
+							routingEngineService.getPath(dstDap.getNodeId(),
+									dstDap.getPortId(),
+									srcDap.getNodeId(),
+									srcDap.getPortId());
+
+					// use static flow entry pusher to push flow mod along in and out path
+					// in: match src client (ip, port), rewrite dest from vip ip/port to member ip/port, forward
+					// out: match dest client (ip, port), rewrite src from member ip/port to vip ip/port, forward
+
+					if (! routeIn.getPath().isEmpty()) {
+						pushStaticVipRoute(true, routeIn, client, member, sw);
+					}
+
+					if (! routeOut.getPath().isEmpty()) {
+						pushStaticVipRoute(false, routeOut, client, member, sw);
+					}
+
+				}
+				iSrcDaps++;
+				iDstDaps++;
+			} else if (srcVsDest < 0) {
+				iSrcDaps++;
+			} else {
+				iDstDaps++;
+			}
+		}
+		return;
+	}
+
+	/**
+	 * used to push given route using static flow entry pusher
+	 * @param boolean inBound
+	 * @param Path route
+	 * @param IPClient client
+	 * @param LBMember member
+	 * @param long pinSwitch
+	 */
+	public void pushStaticVipRoute(boolean inBound, Path route, IPClient client, LBMember member, IOFSwitch pinSwitch) {
+		List<NodePortTuple> path = route.getPath();
+		if (path.size() > 0) {
+			for (int i = 0; i < path.size(); i+=2) {
+				DatapathId sw = path.get(i).getNodeId();
+				String entryName;
+				Match.Builder mb = pinSwitch.getOFFactory().buildMatch();
+				ArrayList<OFAction> actions = new ArrayList<OFAction>();
+
+				OFFlowMod.Builder fmb = pinSwitch.getOFFactory().buildFlowAdd();
+
+				fmb.setIdleTimeout(FlowModUtils.INFINITE_TIMEOUT);
+				fmb.setHardTimeout(FlowModUtils.INFINITE_TIMEOUT);
+				fmb.setBufferId(OFBufferId.NO_BUFFER);
+				fmb.setOutPort(OFPort.ANY);
+				fmb.setCookie(U64.of(0));  
+				fmb.setPriority(FlowModUtils.PRIORITY_MAX);
+
+
+				if (inBound) {
+					entryName = "inbound-vip-"+ member.vipId+"-client-"+client.ipAddress
+							+"-srcport-"+client.srcPort+"-dstport-"+client.targetPort
+							+"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
+					mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
+					.setExact(MatchField.IP_PROTO, client.nw_proto)
+					.setExact(MatchField.IPV4_SRC, client.ipAddress)
+					.setExact(MatchField.IN_PORT, path.get(i).getPortId());
+					if (client.nw_proto.equals(IpProtocol.TCP)) {
+						mb.setExact(MatchField.TCP_SRC, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.UDP)) {
+						mb.setExact(MatchField.UDP_SRC, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.SCTP)) {
+						mb.setExact(MatchField.SCTP_SRC, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.ICMP)) {
+						/* no-op */
+					} else {
+						log.error("Unknown IpProtocol {} detected during inbound static VIP route push.", client.nw_proto);
+					}
+
+
+					if (sw.equals(pinSwitch.getId())) {
+						if (pinSwitch.getOFFactory().getVersion().compareTo(OFVersion.OF_12) < 0) { 
+							actions.add(pinSwitch.getOFFactory().actions().setDlDst(MacAddress.of(member.macString)));
+							actions.add(pinSwitch.getOFFactory().actions().setNwDst(IPv4Address.of(member.address)));
+							actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						} else { // OXM introduced in OF1.2
+							actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ethDst(MacAddress.of(member.macString))));
+							actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ipv4Dst(IPv4Address.of(member.address))));
+							actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						}
+					} else {
+						//fix concurrency errors
+						try{
+							actions.add(switchService.getSwitch(path.get(i+1).getNodeId()).getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						}
+						catch(NullPointerException e){
+							log.error("Fail to install loadbalancer flow rules to offline switch {}.", path.get(i+1).getNodeId());
+						}
+					}
+				} else {
+					entryName = "outbound-vip-"+ member.vipId+"-client-"+client.ipAddress
+							+"-srcport-"+client.srcPort+"-dstport-"+client.targetPort
+							+"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
+					mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
+					.setExact(MatchField.IP_PROTO, client.nw_proto)
+					.setExact(MatchField.IPV4_DST, client.ipAddress)
+					.setExact(MatchField.IN_PORT, path.get(i).getPortId());
+					if (client.nw_proto.equals(IpProtocol.TCP)) {
+						mb.setExact(MatchField.TCP_DST, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.UDP)) {
+						mb.setExact(MatchField.UDP_DST, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.SCTP)) {
+						mb.setExact(MatchField.SCTP_DST, client.srcPort);
+					} else if (client.nw_proto.equals(IpProtocol.ICMP)) {
+						/* no-op */
+					} else {
+						log.error("Unknown IpProtocol {} detected during outbound static VIP route push.", client.nw_proto);
+					}
+
+					if (sw.equals(pinSwitch.getId())) {
+						if (pinSwitch.getOFFactory().getVersion().compareTo(OFVersion.OF_12) < 0) { 
+							actions.add(pinSwitch.getOFFactory().actions().setDlSrc(vips.get(member.vipId).proxyMac));
+							actions.add(pinSwitch.getOFFactory().actions().setNwSrc(IPv4Address.of(vips.get(member.vipId).address)));
+							actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						} else { // OXM introduced in OF1.2
+							actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ethSrc(vips.get(member.vipId).proxyMac)));
+							actions.add(pinSwitch.getOFFactory().actions().setField(pinSwitch.getOFFactory().oxms().ipv4Src(IPv4Address.of(vips.get(member.vipId).address))));
+							actions.add(pinSwitch.getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						}
+					} else {
+						//fix concurrency errors
+						try{
+							actions.add(switchService.getSwitch(path.get(i+1).getNodeId()).getOFFactory().actions().output(path.get(i+1).getPortId(), Integer.MAX_VALUE));
+						}
+						catch(NullPointerException e){
+							log.error("Fail to install loadbalancer flow rules to offline switches {}.", path.get(i+1).getNodeId());
+						}
+					}
+
+				}
+
+
+				fmb.setActions(actions);
+				fmb.setPriority(U16.t(LB_PRIORITY));
+				fmb.setMatch(mb.build());
+				sfpService.addFlow(entryName, fmb.build(), sw);
+			}
+		}
+		return;
+	}
+
+
+	@Override
+	public Collection<LBVip> listVips() {
+		return vips.values();
+	}
+
+	@Override
+	public Collection<LBVip> listVip(String vipId) {
+		Collection<LBVip> result = new HashSet<LBVip>();
+		result.add(vips.get(vipId));
+		return result;
+	}
+
+	@Override
+	public LBVip createVip(LBVip vip) {
+		if (vip == null)
+			vip = new LBVip();
+
+		vips.put(vip.id, vip);
+		vipIpToId.put(vip.address, vip.id);
+		vipIpToMac.put(vip.address, vip.proxyMac);
+
+		return vip;
+	}
+
+	@Override
+	public LBVip updateVip(LBVip vip) {
+		vips.put(vip.id, vip);
+		return vip;
+	}
+
+	@Override
+	public int removeVip(String vipId) {
+		if(vips.containsKey(vipId)){
+			vips.remove(vipId);
+			return 0;
+		} else {
+			return -1;
+		}
+	}
+
+	@Override
+	public Collection<LBPool> listPools() {
+		return pools.values();
+	}
+
+	@Override
+	public Collection<LBPool> listPool(String poolId) {
+		Collection<LBPool> result = new HashSet<LBPool>();
+		result.add(pools.get(poolId));
+		return result;
+	}
+
+	@Override
+	public LBPool createPool(LBPool pool) {
+		if (pool == null)
+			pool = new LBPool();
+
+		pools.put(pool.id, pool);
+		if (pool.vipId != null && vips.containsKey(pool.vipId))
+			vips.get(pool.vipId).pools.add(pool.id);
+		else {
+			log.error("specified vip-id must exist");
+			pool.vipId = null;
+			pools.put(pool.id, pool);
+		}
+		return pool;
+	}
+
+	@Override
+	public LBPool updatePool(LBPool pool) {
+		pools.put(pool.id, pool);
+		return null;
+	}
+
+	@Override
+	public int removePool(String poolId) {
+		LBPool pool;
+		if (pools != null) {
+			pool = pools.get(poolId);
+			if (pool == null)	// fix dereference violations
+				return -1;
+			if (pool.vipId != null && vips.containsKey(pool.vipId))
+				vips.get(pool.vipId).pools.remove(poolId);
+			pools.remove(poolId);
+			return 0;
+		} else {
+			return -1;
+		}
+	}
+
+	@Override
+	public Collection<LBMember> listMembers() {
+		return members.values();
+	}
+
+	@Override
+	public Collection<LBMember> listMember(String memberId) {
+		Collection<LBMember> result = new HashSet<LBMember>();
+		result.add(members.get(memberId));
+		return result;
+	}
+
+	@Override
+	public Collection<LBMember> listMembersByPool(String poolId) {
+		Collection<LBMember> result = new HashSet<LBMember>();
+
+		if(pools.containsKey(poolId)) {
+			ArrayList<String> memberIds = pools.get(poolId).members;
+			if(memberIds !=null && members != null){
+				for (int i = 0; i<memberIds.size(); i++)
+					result.add(members.get(memberIds.get(i)));
+			}
+		}
+		return result;
+	}
+
+	@Override
+	public LBMember createMember(LBMember member) {
+		if (member == null)
+			member = new LBMember();
+
+		members.put(member.id, member);
+		memberIpToId.put(member.address, member.id);
+
+		if (member.poolId != null && pools.get(member.poolId) != null) {
+			member.vipId = pools.get(member.poolId).vipId;
+			if (!pools.get(member.poolId).members.contains(member.id))
+				pools.get(member.poolId).members.add(member.id);
+		} else
+			log.error("member must be specified with non-null pool_id");
+
+		return member;
+	}
+
+	@Override
+	public LBMember updateMember(LBMember member) {
+		members.put(member.id, member);
+		return member;
+	}
+
+	@Override
+	public int removeMember(String memberId) {
+		LBMember member;
+		member = members.get(memberId);
+
+		if(member != null){
+			if (member.poolId != null && pools.containsKey(member.poolId))
+				pools.get(member.poolId).members.remove(memberId);
+			members.remove(memberId);
+			return 0;
+		} else {
+			return -1;
+		}    
+	}
+
+	@Override
+	public Collection<LBMonitor> listMonitors() {
+		return null;
+	}
+
+	@Override
+	public Collection<LBMonitor> listMonitor(String monitorId) {
+		return null;
+	}
+
+	@Override
+	public LBMonitor createMonitor(LBMonitor monitor) {
+		return null;
+	}
+
+	@Override
+	public LBMonitor updateMonitor(LBMonitor monitor) {
+		return null;
+	}
+
+	@Override
+	public int removeMonitor(String monitorId) {
+		return 0;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>>
+	getModuleServices() {
+		Collection<Class<? extends IFloodlightService>> l = 
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(ILoadBalancerService.class);
+		return l;
+	}
+
+	@Override
+	public Map<Class<? extends IFloodlightService>, IFloodlightService>
+	getServiceImpls() {
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m = 
+				new HashMap<Class<? extends IFloodlightService>,
+				IFloodlightService>();
+		m.put(ILoadBalancerService.class, this);
+		return m;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>>
+	getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l = 
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IFloodlightProviderService.class);
+		l.add(IRestApiService.class);
+		l.add(IOFSwitchService.class);
+		l.add(IDeviceService.class);
+		l.add(IDebugCounterService.class);
+		l.add(ITopologyService.class);
+		l.add(IRoutingService.class);
+		l.add(IStaticEntryPusherService.class);
+
+		return l;
+	}
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
+		restApiService = context.getServiceImpl(IRestApiService.class);
+		debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+		deviceManagerService = context.getServiceImpl(IDeviceService.class);
+		routingEngineService = context.getServiceImpl(IRoutingService.class);
+		topologyService = context.getServiceImpl(ITopologyService.class);
+		sfpService = context.getServiceImpl(IStaticEntryPusherService.class);
+		switchService = context.getServiceImpl(IOFSwitchService.class);
+		statisticsService = context.getServiceImpl(IStatisticsService.class);
+
+
+		vips = new HashMap<String, LBVip>();
+		pools = new HashMap<String, LBPool>();
+		members = new HashMap<String, LBMember>();
+		vipIpToId = new HashMap<Integer, String>();
+		vipIpToMac = new HashMap<Integer, MacAddress>();
+		memberIpToId = new HashMap<Integer, String>();
+		deviceToMemberId = new HashMap<IDevice, String>();
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
+		restApiService.addRestletRoutable(new LoadBalancerWebRoutable());
+		debugCounterService.registerModule(this.getName());
+		counterPacketOut = debugCounterService.registerCounter(this.getName(), "packet-outs-written", "Packet outs written by the LoadBalancer", MetaData.WARN);
+		statisticsService.collectStatistics(true);
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/PoolsResource.java b/src/main/java/net/floodlightcontroller/loadbalancer/PoolsResource.java
index 82a041a20..3288a8d31 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/PoolsResource.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/PoolsResource.java
@@ -128,7 +128,12 @@ public class PoolsResource extends ServerResource {
                 continue;
             }
             if (n.equals("lb_method")) {
-                pool.lbMethod = Short.parseShort(jp.getText());
+            	String method = jp.getText();
+            	if(method.equalsIgnoreCase("RR")){
+            		pool.lbMethod = (short) 1;
+            	} else if(method.equalsIgnoreCase("STATISTICS")){
+            		pool.lbMethod = (short) 2;
+            	}
                 continue;
             }
             if (n.equals("protocol")) {
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index d8f25e51f..87e5b868a 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -63,7 +63,7 @@ net.floodlightcontroller.restserver.RestApiServer.useHttp=YES
 net.floodlightcontroller.restserver.RestApiServer.httpsPort=8081
 net.floodlightcontroller.restserver.RestApiServer.httpPort=8080
 net.floodlightcontroller.restserver.RestApiServer.accessControlAllowAllOrigins=TRUE
-net.floodlightcontroller.statistics.StatisticsCollector.enable=FALSE
+net.floodlightcontroller.statistics.StatisticsCollector.enable=TRUE
 net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10
 net.floodlightcontroller.topology.TopologyManager.pathMetric=latency
 net.floodlightcontroller.topology.TopologyManager.maxPathsToCompute=3
-- 
GitLab