diff --git a/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java b/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java index 6b1bb11b721..d8d2e8396f9 100644 --- a/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java +++ b/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java @@ -25,12 +25,18 @@ import com.cloud.agent.api.to.LoadBalancerTO; */ public class LoadBalancerConfigCommand extends RoutingCommand { LoadBalancerTO[] loadBalancers; + String routerIp; - public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers) { + public LoadBalancerConfigCommand(String routerControlIpAddress, LoadBalancerTO[] loadBalancers) { this.loadBalancers = loadBalancers; + this.routerIp = routerControlIpAddress; } - public LoadBalancerTO[] getLoadBalancers() { + public String getRouterIp() { + return routerIp; + } + + public LoadBalancerTO[] getLoadBalancers() { return loadBalancers; } } diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 1038207b0e9..cd09d803eb0 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -125,6 +125,7 @@ import com.cloud.agent.api.routing.DhcpEntryCommand; import com.cloud.agent.api.routing.IPAssocCommand; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.LoadBalancerCfgCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.RoutingCommand; import com.cloud.agent.api.routing.SavePasswordCommand; @@ -153,6 +154,8 @@ import com.cloud.agent.api.to.VolumeTO; import com.cloud.exception.InternalErrorException; import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.HAProxyConfigurator; +import com.cloud.network.LoadBalancerConfigurator; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.resource.ServerResource; @@ -349,6 +352,8 @@ public abstract class CitrixResourceBase implements ServerResource { return execute((SetPortForwardingRulesCommand) cmd); } else if (cmd instanceof LoadBalancerCfgCommand) { return execute((LoadBalancerCfgCommand) cmd); + } else if (cmd instanceof LoadBalancerConfigCommand) { + return execute((LoadBalancerConfigCommand) cmd); } else if (cmd instanceof IPAssocCommand) { return execute((IPAssocCommand) cmd); } else if (cmd instanceof CheckConsoleProxyLoadCommand) { @@ -1032,6 +1037,66 @@ public abstract class CitrixResourceBase implements ServerResource { return new Answer(cmd); } + + protected Answer execute(final LoadBalancerConfigCommand cmd) { + Connection conn = getConnection(); + String routerIp = cmd.getRouterIp(); + + if (routerIp == null) { + return new Answer(cmd); + } + + LoadBalancerConfigurator cfgtr = new HAProxyConfigurator(); + String[] config = cfgtr.generateConfiguration(cmd); + String[][] rules = cfgtr.generateFwRules(cmd); + String tmpCfgFilePath = "/tmp/" + cmd.getRouterIp().replace('.', '_') + ".cfg"; + String tmpCfgFileContents = ""; + for (int i = 0; i < config.length; i++) { + tmpCfgFileContents += config[i]; + tmpCfgFileContents += "\n"; + } + + String result = callHostPlugin(conn, "vmops", "createFile", "filepath", tmpCfgFilePath, "filecontents", tmpCfgFileContents); + + if (result == null || result.isEmpty()) { + return new Answer(cmd, false, "LoadBalancerConfigCommand failed to create HA proxy cfg file."); + } + + String[] addRules = rules[LoadBalancerConfigurator.ADD]; + String[] removeRules = rules[LoadBalancerConfigurator.REMOVE]; + + String args = ""; + args += "-i " + routerIp; + args += " -f " + tmpCfgFilePath; + + StringBuilder sb = new StringBuilder(); + if (addRules.length > 0) { + for (int i = 0; i < addRules.length; i++) { + sb.append(addRules[i]).append(','); + } + + args += " -a " + sb.toString(); + } + + sb = new StringBuilder(); + if (removeRules.length > 0) { + for (int i = 0; i < removeRules.length; i++) { + sb.append(removeRules[i]).append(','); + } + + args += " -d " + sb.toString(); + } + + result = callHostPlugin(conn, "vmops", "setLoadBalancerRule", "args", args); + + if (result == null || result.isEmpty()) { + return new Answer(cmd, false, "LoadBalancerConfigCommand failed"); + } + + callHostPlugin(conn, "vmops", "deleteFile", "filepath", tmpCfgFilePath); + + return new Answer(cmd); + } protected synchronized Answer execute(final DhcpEntryCommand cmd) { Connection conn = getConnection(); diff --git a/server/src/com/cloud/network/HAProxyConfigurator.java b/core/src/com/cloud/network/HAProxyConfigurator.java similarity index 64% rename from server/src/com/cloud/network/HAProxyConfigurator.java rename to core/src/com/cloud/network/HAProxyConfigurator.java index 2a834a67436..1fe17880039 100644 --- a/server/src/com/cloud/network/HAProxyConfigurator.java +++ b/core/src/com/cloud/network/HAProxyConfigurator.java @@ -25,7 +25,10 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO; import com.cloud.utils.net.NetUtils; @@ -143,6 +146,47 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { result.add(getBlankLine()); return result; } + + private List getRulesForPool(LoadBalancerTO lbTO) { + StringBuilder sb = new StringBuilder(); + String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString(); + String publicIP = lbTO.getSrcIp(); + String publicPort = Integer.toString(lbTO.getSrcPort()); + String algorithm = lbTO.getAlgorithm(); + + List result = new ArrayList(); + //add line like this: "listen 65_37_141_30-80 65.37.141.30:80" + sb = new StringBuilder(); + sb.append("listen ").append(poolName).append(" ") + .append(publicIP).append(":").append(publicPort); + result.add(sb.toString()); + sb = new StringBuilder(); + sb.append("\t").append("balance ").append(algorithm); + result.add(sb.toString()); + if (publicPort.equals(NetUtils.HTTP_PORT)) { + sb = new StringBuilder(); + sb.append("\t").append("mode http"); + result.add(sb.toString()); + sb = new StringBuilder(); + sb.append("\t").append("option httpclose"); + result.add(sb.toString()); + } + int i=0; + for (DestinationTO dest: lbTO.getDestinations()) { + //add line like this: "server 65_37_141_30-80_3 10.1.1.4:80 check" + if (dest.isRevoked()) { + continue; + } + sb = new StringBuilder(); + sb.append("\t").append("server ").append(poolName) + .append("_").append(Integer.toString(i++)).append(" ") + .append(dest.getDestIp()).append(":").append(dest.getDestPort()) + .append(" check"); + result.add(sb.toString()); + } + result.add(getBlankLine()); + return result; + } private String getBlankLine() { return new String("\t "); @@ -176,4 +220,53 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { return result; } + + @Override + public String[] generateConfiguration(LoadBalancerConfigCommand lbCmd) { + List result = new ArrayList(); + + result.addAll(Arrays.asList(globalSection)); + result.add(getBlankLine()); + result.addAll(Arrays.asList(defaultsSection)); + result.add(getBlankLine()); + + if (lbCmd.getLoadBalancers().length == 0){ + //haproxy cannot handle empty listen / frontend or backend, so add a dummy listener + //on port 9 + result.addAll(Arrays.asList(defaultListen)); + } + result.add(getBlankLine()); + + for (LoadBalancerTO lbTO: lbCmd.getLoadBalancers()){ + List poolRules = getRulesForPool(lbTO); + result.addAll(poolRules); + } + + return result.toArray(new String[result.size()]); + } + + @Override + public String[][] generateFwRules(LoadBalancerConfigCommand lbCmd) { + String [][] result = new String [2][]; + Set toAdd = new HashSet(); + Set toRemove = new HashSet(); + + for (LoadBalancerTO lbTO: lbCmd.getLoadBalancers()) { + + StringBuilder sb = new StringBuilder(); + sb.append(lbTO.getSrcIp()).append(":"); + sb.append(lbTO.getSrcPort()).append(":"); + String lbRuleEntry = sb.toString(); + if (!lbTO.isRevoked()) { + toAdd.add(lbRuleEntry); + } else { + toRemove.add(lbRuleEntry); + } + } + toRemove.removeAll(toAdd); + result[ADD] = toAdd.toArray(new String[toAdd.size()]); + result[REMOVE] = toRemove.toArray(new String[toRemove.size()]); + + return result; + } } diff --git a/core/src/com/cloud/network/LoadBalancerConfigurator.java b/core/src/com/cloud/network/LoadBalancerConfigurator.java index f79ae8f15f4..ad2b3d7ddda 100644 --- a/core/src/com/cloud/network/LoadBalancerConfigurator.java +++ b/core/src/com/cloud/network/LoadBalancerConfigurator.java @@ -19,6 +19,7 @@ package com.cloud.network; import java.util.List; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.to.PortForwardingRuleTO; @@ -32,4 +33,7 @@ public interface LoadBalancerConfigurator { public String [] generateConfiguration(List fwRules); public String [][] generateFwRules(List fwRules); + + public String [] generateConfiguration(LoadBalancerConfigCommand lbCmd); + public String [][] generateFwRules(LoadBalancerConfigCommand lbCmd); } diff --git a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java index 129e01989ff..36cc2e8795f 100644 --- a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java +++ b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java @@ -2099,8 +2099,16 @@ public class DomainRouterManagerImpl implements DomainRouterManager, DomainRoute @Override public boolean applyLBRules(Network network, List rules) { + DomainRouterVO router = _routerDao.findByNetworkConfiguration(network.getId()); + if (router == null) { + s_logger.warn("Unable to apply lb rules, virtual router doesn't exist in the network " + network.getId()); + throw new ResourceUnavailableException("Unable to apply lb rules"); + } + + String routerControlIpAddress = router.getPrivateIpAddress(); - DomainRouterVO router = _routerDao.findByNetworkConfiguration(network.getId()); + if (router.getState() == State.Running || router.getState() == State.Starting) { + Commands cmds = new Commands(OnError.Continue); LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()]; int i = 0;