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