Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class LoadBalancerConfigCommand extends NetworkElementCommand {
public String lbStatsAuth = "admin1:AdMiN123";
public String lbStatsUri = "/admin?stats";
public String maxconn = "";
public Long idleTimeout = 50000L; /* 0=infinite, >0 = timeout in milliseconds */
public String lbProtocol;
public boolean keepAliveEnabled = false;
NicTO nic;
Expand All @@ -50,7 +51,7 @@ public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, Long vpcId) {
}

public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, String publicIp, String guestIp, String privateIp, NicTO nic, Long vpcId, String maxconn,
boolean keepAliveEnabled) {
boolean keepAliveEnabled, Long idleTimeout) {
this.loadBalancers = loadBalancers;
this.lbStatsPublicIP = publicIp;
this.lbStatsPrivateIP = privateIp;
Expand All @@ -59,6 +60,7 @@ public LoadBalancerConfigCommand(LoadBalancerTO[] loadBalancers, String publicIp
this.vpcId = vpcId;
this.maxconn = maxconn;
this.keepAliveEnabled = keepAliveEnabled;
this.idleTimeout = idleTimeout;
}

public NicTO getNic() {
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/com/cloud/network/HAProxyConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,19 @@ public String[] generateConfiguration(final LoadBalancerConfigCommand lbCmd) {
if (lbCmd.keepAliveEnabled) {
dSection.set(7, "\tno option httpclose");
}
if (lbCmd.idleTimeout > 0) {
dSection.set(9, "\ttimeout client " + Long.toString(lbCmd.idleTimeout));
dSection.set(10, "\ttimeout server " + Long.toString(lbCmd.idleTimeout));
} else if (lbCmd.idleTimeout == 0) {
// .remove() is not allowed, only .set() operations are allowed as the list
// is a fixed size. So lets just mark the entry as blank.
dSection.set(9, "");
dSection.set(10, "");
} else {
// Negative idleTimeout values are considered invalid; retain the
// default HAProxy timeout values from defaultsSection for predictability.
logger.warn("Negative idleTimeout ({}) configured; retaining default HAProxy timeouts.", lbCmd.idleTimeout);
}

if (logger.isDebugEnabled()) {
for (final String s : dSection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() {
lbs.toArray(arrayLbs);

final NicTO nic = new NicTO();
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false);
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false, 0L);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2");
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand1() {
final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()];
lbs.toArray(arrayLbs);
final NicTO nic = new NicTO();
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false);
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false, 50000L);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2");
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME);
return cmd;
Expand All @@ -795,7 +795,7 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand2() {
lbs.toArray(arrayLbs);
final NicTO nic = new NicTO();
nic.setIp("10.1.10.2");
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false);
final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false, 50000L);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2");
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME);
return cmd;
Expand Down
21 changes: 16 additions & 5 deletions core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,24 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() {
LoadBalancerTO[] lba = new LoadBalancerTO[1];
lba[0] = lb;
HAProxyConfigurator hpg = new HAProxyConfigurator();
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L);
String result = genConfig(hpg, cmd);
assertTrue("keepalive disabled should result in 'option httpclose' in the resulting haproxy config", result.contains("\toption httpclose"));

cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true);
cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true, 0L);
result = genConfig(hpg, cmd);
assertTrue("keepalive enabled should result in 'no option httpclose' in the resulting haproxy config", result.contains("\tno option httpclose"));

cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true, 0L);
result = genConfig(hpg, cmd);
assertTrue("idleTimeout of 0 should not generate 'timeout server' in the resulting haproxy config", !result.contains("\ttimeout server"));
assertTrue("idleTimeout of 0 should not generate 'timeout client' in the resulting haproxy config", !result.contains("\ttimeout client"));

cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true, 1234L);
result = genConfig(hpg, cmd);
assertTrue("idleTimeout of 1234 should result in 'timeout server 1234' in the resulting haproxy config", result.contains("\ttimeout server 1234"));
assertTrue("idleTimeout of 1234 should result in 'timeout client 1234' in the resulting haproxy config", result.contains("\ttimeout client 1234"));

// TODO
// create lb command
// setup tests for
Expand All @@ -106,7 +117,7 @@ public void testGenerateConfigurationLoadBalancerProxyProtocolConfigCommand() {
LoadBalancerTO[] lba = new LoadBalancerTO[1];
lba[0] = lb;
HAProxyConfigurator hpg = new HAProxyConfigurator();
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L);
String result = genConfig(hpg, cmd);
assertTrue("'send-proxy' should result if protocol is 'tcp-proxy'", result.contains("send-proxy"));
}
Expand All @@ -118,7 +129,7 @@ public void generateConfigurationTestWithCidrList() {
LoadBalancerTO[] lba = new LoadBalancerTO[1];
lba[0] = lb;
HAProxyConfigurator hpg = new HAProxyConfigurator();
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L);
String result = genConfig(hpg, cmd);
Assert.assertTrue(result.contains("acl network_allowed src 1.1.1.1 2.2.2.2/24 \n\ttcp-request connection reject if !network_allowed"));
}
Expand All @@ -131,7 +142,7 @@ public void generateConfigurationTestWithSslCert() {
LoadBalancerTO[] lba = new LoadBalancerTO[1];
lba[0] = lb;
HAProxyConfigurator hpg = new HAProxyConfigurator();
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false, 0L);
String result = genConfig(hpg, cmd);
Assert.assertTrue(result.contains("bind 10.2.0.1:443 ssl crt /etc/cloudstack/ssl/10_2_0_1-443.pem"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ public interface NetworkOrchestrationService {
"Load Balancer(haproxy) maximum number of concurrent connections(global max)",
true,
Scope.Global);
ConfigKey<Long> NETWORK_LB_HAPROXY_IDLE_TIMEOUT = new ConfigKey<>(
"Network",
Long.class,
"network.loadbalancer.haproxy.idle.timeout",
"50000",
"Load Balancer(haproxy) idle timeout in milliseconds. Use 0 for infinite.",
true,
Scope.Global);

List<? extends Network> setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault)
throws ConcurrentOperationException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4919,6 +4919,7 @@ public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, DeniedRoutes,
GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion,
PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RollingRestartEnabled,
TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED, NETWORK_LB_HAPROXY_MAX_CONN};
TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED, NETWORK_LB_HAPROXY_MAX_CONN,
NETWORK_LB_HAPROXY_IDLE_TIMEOUT};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ private void createApplyLoadBalancingRulesCommands(List<LoadBalancingRule> rules
maxconn = offering.getConcurrentConnections().toString();
}
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lbs, elbVm.getPublicIpAddress(), _nicDao.getIpAddress(guestNetworkId, elbVm.getId()),
elbVm.getPrivateIpAddress(), null, null, maxconn, offering.isKeepAliveEnabled());
elbVm.getPrivateIpAddress(), null, null, maxconn, offering.isKeepAliveEnabled(),
NetworkOrchestrationService.NETWORK_LB_HAPROXY_IDLE_TIMEOUT.value());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, elbVm.getPrivateIpAddress());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, elbVm.getInstanceName());
//FIXME: why are we setting attributes directly? Ick!! There should be accessors and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@ private void createApplyLoadBalancingRulesCommands(final List<LoadBalancingRule>
}
final LoadBalancerConfigCommand cmd =
new LoadBalancerConfigCommand(lbs, guestNic.getIPv4Address(), guestNic.getIPv4Address(), internalLbVm.getPrivateIpAddress(), _itMgr.toNicTO(guestNicProfile,
internalLbVm.getHypervisorType()), internalLbVm.getVpcId(), maxconn, offering.isKeepAliveEnabled());
internalLbVm.getHypervisorType()), internalLbVm.getVpcId(), maxconn, offering.isKeepAliveEnabled(),
NetworkOrchestrationService.NETWORK_LB_HAPROXY_IDLE_TIMEOUT.value());

cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key());
cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ public void createApplyLoadBalancingRulesCommands(final List<LoadBalancingRule>
}

final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lbs, routerPublicIp, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()),
router.getPrivateIpAddress(), _itMgr.toNicTO(nicProfile, router.getHypervisorType()), router.getVpcId(), maxconn, offering.isKeepAliveEnabled());
router.getPrivateIpAddress(), _itMgr.toNicTO(nicProfile, router.getHypervisorType()), router.getVpcId(), maxconn, offering.isKeepAliveEnabled(),
NetworkOrchestrationService.NETWORK_LB_HAPROXY_IDLE_TIMEOUT.value());

cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key());
cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1688,7 +1688,7 @@ private void updateWithLbRules(final DomainRouterJoinVO routerJoinVO, final Stri
} else {
loadBalancingData.append("maxconn=").append(offering.getConcurrentConnections());
}

loadBalancingData.append(",idletimeout=").append(NetworkOrchestrationService.NETWORK_LB_HAPROXY_IDLE_TIMEOUT.value());
loadBalancingData.append(",sourcePortStart=").append(firewallRuleVO.getSourcePortStart())
.append(",sourcePortEnd=").append(firewallRuleVO.getSourcePortEnd());
if (firewallRuleVO instanceof LoadBalancerVO) {
Expand Down
43 changes: 42 additions & 1 deletion systemvm/debian/root/health_checks/haproxy_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ def checkMaxconn(haproxyData, haCfgSections):

return True

def checkIdletimeout(haproxyData, haCfgSections):
if "idletimeout" not in haproxyData:
return True

# Normalize idletimeout value to string for comparison
idle_value = str(haproxyData["idletimeout"]).strip()

# Safely get the defaults section and its timeout directives
defaults_section = haCfgSections.get("defaults", {})
timeout_lines = defaults_section.get("timeout", [])

# Extract client and server timeout values from the parsed "timeout" entries
timeout_values = {}
for tline in timeout_lines:
tline = tline.strip()
if not tline:
continue
parts = tline.split(None, 1)
if len(parts) < 2:
continue
kind, value = parts[0].strip(), parts[1].strip()
if kind in ("client", "server"):
timeout_values[kind] = value

# Special handling for idletimeout == 0: there should be no client/server timeouts configured
if idle_value == "0":
if "client" in timeout_values or "server" in timeout_values:
print("defaults timeout client or timeout server should be absent when idletimeout is 0")
return False
return True

# Non-zero idletimeout: both client and server timeouts must be present
if "client" not in timeout_values or "server" not in timeout_values:
print("defaults timeout client or timeout server missing")
return False

if idle_value != timeout_values["client"] or idle_value != timeout_values["server"]:
print("defaults timeout client or timeout server mismatch occurred")
return False
return True

def checkLoadBalance(haproxyData, haCfgSections):
correct = True
Expand Down Expand Up @@ -120,9 +160,10 @@ def main():
currSectionDict[lineSec[0]].append(lineSec[1] if len(lineSec) > 1 else '')

checkMaxConn = checkMaxconn(haproxyData[0], haCfgSections)
checkIdleTimeout = checkIdletimeout(haproxyData[0], haCfgSections)
checkLbRules = checkLoadBalance(haproxyData, haCfgSections)

if checkMaxConn and checkLbRules:
if checkMaxConn and checkIdleTimeout and checkLbRules:
print("All checks pass")
exit(0)
else:
Expand Down
Loading