Skip to content

Commit

Permalink
CLOUDSTACK-9282: VPC Inline LB
Browse files Browse the repository at this point in the history
Co-Authored-By: Frank Maximus <frank.maximus@nuagenetworks.net>
Co-Authored-By: Prashanth Manthena prashanth.manthena@nuagenetworks.net

Bug: https://issues.apache.org/jira/browse/CLOUDSTACK-9282

Design-Doc: https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=61340894
  • Loading branch information
urvis authored and Prashanth Manthena committed Mar 21, 2017
1 parent 1decf53 commit b88ec4b
Show file tree
Hide file tree
Showing 68 changed files with 9,586 additions and 140 deletions.
1 change: 1 addition & 0 deletions api/src/com/cloud/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public static class Provider {
public static final Provider ExternalDhcpServer = new Provider("ExternalDhcpServer", true);
public static final Provider ExternalGateWay = new Provider("ExternalGateWay", true);
public static final Provider ElasticLoadBalancerVm = new Provider("ElasticLoadBalancerVm", false);
public static final Provider VpcInlineLbVm = new Provider("VpcInlineLbVm", false);
public static final Provider SecurityGroupProvider = new Provider("SecurityGroupProvider", false);
public static final Provider VPCVirtualRouter = new Provider("VpcVirtualRouter", false);
public static final Provider None = new Provider("None", false);
Expand Down
2 changes: 1 addition & 1 deletion api/src/com/cloud/network/VirtualRouterProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

public interface VirtualRouterProvider extends InternalIdentity, Identity {
public enum Type {
VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm
VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm, VpcInlineLbVm
}

public Type getType();
Expand Down
1 change: 1 addition & 0 deletions api/src/com/cloud/offering/ServiceOffering.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface ServiceOffering extends DiskOffering, InfrastructureEntity, Int
public static final String routerDefaultOffUniqueName = "Cloud.Com-SoftwareRouter";
public static final String elbVmDefaultOffUniqueName = "Cloud.Com-ElasticLBVm";
public static final String internalLbVmDefaultOffUniqueName = "Cloud.Com-InternalLBVm";
public static final String vpcInlineLbVmDefaultOffUniqueName = "Cloud.Com-VpcInlineLBVm";

public enum StorageType {
local, shared
Expand Down
2 changes: 1 addition & 1 deletion api/src/com/cloud/vm/VirtualMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public enum Event {
};

public enum Type {
User(false), DomainRouter(true), ConsoleProxy(true), SecondaryStorageVm(true), ElasticIpVm(true), ElasticLoadBalancerVm(true), InternalLoadBalancerVm(true),
User(false), DomainRouter(true), ConsoleProxy(true), SecondaryStorageVm(true), ElasticIpVm(true), ElasticLoadBalancerVm(true), InternalLoadBalancerVm(true), VpcInlineLoadBalancerVm(true),

/*
* UserBareMetal is only used for selecting VirtualMachineGuru, there is no
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ public void setVpcName(String vpcName) {
this.vpcName = vpcName;
}

public Set<NicResponse> getNics() {
return this.nics;
}

public void setNics(Set<NicResponse> nics) {
this.nics = nics;
}
Expand Down
5 changes: 5 additions & 0 deletions client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@
<artifactId>cloud-plugin-network-internallb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-network-vpcinlinelb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-network-vxlan</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public List<ConfigItem> generateConfig(final NetworkElementCommand cmd) {
for (final IpAddressTO ip : command.getIpAddresses()) {
final IpAddress ipAddress = new IpAddress(ip.getPublicIp(), ip.isSourceNat(), ip.isAdd(), ip.isOneToOneNat(), ip.isFirstIP(), ip.getVlanGateway(), ip.getVlanNetmask(),
ip.getVifMacAddress(), ip.getNicDevId(), ip.isNewNic(), ip.getTrafficType().toString());
if (ip.getTrafficType() != null) {
ipAddress.setNwType(ip.getTrafficType().name().toLowerCase());
}
ips.add(ipAddress);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,11 @@ public void setNewNic(boolean newNic) {
this.newNic = newNic;
}

public String getNwType() {
return nwType;
}

public void setNwType(String nwType) {
this.nwType = nwType;
}
}
101 changes: 84 additions & 17 deletions core/src/com/cloud/network/HAProxyConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.mutable.MutableBoolean;
import org.apache.log4j.Logger;

import static com.cloud.agent.api.to.LoadBalancerTO.HealthCheckPolicyTO;
import static com.google.common.base.Predicates.not;

import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
import com.cloud.agent.api.to.LoadBalancerTO;
import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO;
Expand Down Expand Up @@ -299,7 +307,7 @@ backends are subject to be used by the same clients (eg:
Example :
appsession JSESSIONID len 52 timeout 3h
*/
private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO, MutableBoolean httpbasedStickiness) {
int i = 0;

if (lbTO.getStickinessPolicies() == null) {
Expand Down Expand Up @@ -377,6 +385,7 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
if (domainSb != null) {
sb.append(domainSb).append(" ");
}
httpbasedStickiness.setValue(true);
} else if (StickinessMethodType.SourceBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
/* Default Values */
String tablesize = "200k"; // optional
Expand Down Expand Up @@ -450,6 +459,7 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
if (mode != null) {
sb.append("mode ").append(mode).append(" ");
}
httpbasedStickiness.setValue(true);
} else {
/*
* Error is silently swallowed.
Expand All @@ -466,25 +476,85 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
return sb.toString();
}

private String getLbSubRuleForHealthCheck(final LoadBalancerTO lbTO, List<String> result, MutableBoolean httpBasedHealthCheck) {
// Only support one policy, get the first non-revoked
Optional<HealthCheckPolicyTO> maybeHealthCheck = Optional.absent();

if (ArrayUtils.isNotEmpty(lbTO.getHealthCheckPolicies())) {
final List<HealthCheckPolicyTO> ts = Arrays.asList(lbTO.getHealthCheckPolicies());
maybeHealthCheck = Iterables.tryFind(ts, not(HealthCheckPolicyTO::isRevoked));
}

if (maybeHealthCheck.isPresent()) {

HealthCheckPolicyTO healthCheck = maybeHealthCheck.get();

StringBuilder sb = new StringBuilder();

final String healthCheckPath = healthCheck.getpingPath();

if (healthCheckPath != null) {
sb.append("\t").append("option httpchk");

// Enable http check
if (!healthCheckPath.isEmpty()) {
httpBasedHealthCheck.setValue(true);
sb.append(" ").append(healthCheckPath);
}

result.add(sb.toString());

sb = new StringBuilder("\tdefault-server");

if (healthCheck.getHealthcheckInterval() > 0) {
sb.append(" inter ").append(healthCheck.getHealthcheckInterval()).append("s");
}
if (healthCheck.getHealthcheckThresshold() > 0) {
sb.append(" rise ").append(healthCheck.getHealthcheckThresshold());
}
if (healthCheck.getUnhealthThresshold() > 0) {
sb.append(" fall ").append(healthCheck.getUnhealthThresshold());
}

result.add(sb.toString());
}

if (healthCheck.getResponseTime() > 0) {
result.add("\ttimeout check " + healthCheck.getResponseTime() + "s");
}

return null;
} else {
return null;
}
}

private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) {
StringBuilder sb = new StringBuilder();
final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
final String publicIP = lbTO.getSrcIp();
final String publicPort = Integer.toString(lbTO.getSrcPort());
final String algorithm = lbTO.getAlgorithm();

final List<String> result = new ArrayList<String>();
final List<String> result = new LinkedList<String>();
// 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());

final MutableBoolean httpBasedHealthCheck = new MutableBoolean(false);
final String healthCheckSubRule = getLbSubRuleForHealthCheck(lbTO, result, httpBasedHealthCheck);

final MutableBoolean httpbasedStickiness = new MutableBoolean(false);
final String stickinessSubRule = getLbSubRuleForStickiness(lbTO, httpbasedStickiness);

int i = 0;
Boolean destsAvailable = false;
final String stickinessSubRule = getLbSubRuleForStickiness(lbTO);

final List<String> dstSubRule = new ArrayList<String>();
final List<String> dstWithCookieSubRule = new ArrayList<String>();
for (final DestinationTO dest : lbTO.getDestinations()) {
Expand All @@ -506,6 +576,11 @@ private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean ke
if(lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals("tcp-proxy")) {
sb.append(" send-proxy");
}

if (healthCheckSubRule != null) {
sb.append(healthCheckSubRule);
}

dstSubRule.add(sb.toString());
if (stickinessSubRule != null) {
sb.append(" cookie ").append(dest.getDestIp().replace(".", "_")).append('-').append(dest.getDestPort()).toString();
Expand All @@ -514,19 +589,9 @@ private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean ke
destsAvailable = true;
}

Boolean httpbasedStickiness = false;
/* attach stickiness sub rule only if the destinations are available */
if (stickinessSubRule != null && destsAvailable == true) {
for (final StickinessPolicyTO stickinessPolicy : lbTO.getStickinessPolicies()) {
if (stickinessPolicy == null) {
continue;
}
if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) ||
StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
httpbasedStickiness = true;
}
}
if (httpbasedStickiness) {
if (stickinessSubRule != null && destsAvailable) {
if (httpbasedStickiness.isTrue()) {
result.addAll(dstWithCookieSubRule);
} else {
result.addAll(dstSubRule);
Expand All @@ -535,10 +600,12 @@ private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean ke
} else {
result.addAll(dstSubRule);
}

if (stickinessSubRule != null && !destsAvailable) {
s_logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause: backends are unavailable");
}
if (publicPort.equals(NetUtils.HTTP_PORT) && !keepAliveEnabled || httpbasedStickiness) {

if (publicPort.equals(NetUtils.HTTP_PORT) && !keepAliveEnabled || httpbasedStickiness.isTrue() || httpBasedHealthCheck.isTrue()) {
sb = new StringBuilder();
sb.append("\t").append("mode http");
result.add(sb.toString());
Expand Down Expand Up @@ -572,7 +639,7 @@ private String generateStatsRule(final LoadBalancerConfigCommand lbCmd, final St

@Override
public String[] generateConfiguration(final LoadBalancerConfigCommand lbCmd) {
final List<String> result = new ArrayList<String>();
final List<String> result = new LinkedList<String>();
final List<String> gSection = Arrays.asList(globalSection);
// note that this is overwritten on the String in the static ArrayList<String>
gSection.set(2, "\tmaxconn " + lbCmd.maxconn);
Expand Down
76 changes: 72 additions & 4 deletions core/test/com/cloud/network/HAProxyConfiguratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,28 @@

package com.cloud.network;

import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import static com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy;
import static com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertTrue;

import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
import com.cloud.agent.api.to.LoadBalancerTO;
import com.cloud.network.lb.LoadBalancingRule.LbDestination;

import java.util.List;
import java.util.ArrayList;
import com.cloud.network.rules.LbStickinessMethod;
import com.cloud.utils.Pair;

/**
* @author dhoogland
Expand Down Expand Up @@ -91,6 +99,66 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() {
// httpmode
}

@Test
public void testGenerateConfigurationLoadBalancerHealthCheckCommand() {
List<LbHealthCheckPolicy> healthCheckPolicies = Arrays.asList(new LbHealthCheckPolicy("/", "health", 5, 5, 5, 5));
LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, null, null, healthCheckPolicies, null, null);
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", true);
String result = genConfig(hpg, cmd);
assertThat("keepalive enabled or http healthcheck should result in 'mode http' in the resulting haproxy config", result, containsString("mode http"));
assertThat("healthcheck should result in 'option httpchk' in the resulting haproxy config", result, containsString("option httpchk /"));

cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true);
healthCheckPolicies.set(0, new LbHealthCheckPolicy("/", "health", 5, 5, 5, 5, true));
lba[0] = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, null, null, healthCheckPolicies, null, null);
result = genConfig(hpg, cmd);
assertThat("revoked healthcheck should not result in 'option httpchk' in the resulting haproxy config", result, not(containsString("option httpchk")));
assertThat("keepalive enabled should not result in 'mode http' in the resulting haproxy config", result, not(containsString("mode http")));

lba[0] = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, null, null);
result = genConfig(hpg, cmd);
assertThat("revoked healthcheck should not result in 'option httpchk' in the resulting haproxy config", result, not(containsString("option httpchk")));
assertThat("keepalive enabled should not result in 'mode http' in the resulting haproxy config", result, not(containsString("mode http")));
}

@Test
public void testGenerateConfigurationLoadBalancerStickinessCommand() {
final List<LbDestination> dests = Arrays.asList(
new LbDestination(443, 8443, "10.1.10.2", false),
new LbDestination(443, 8443, "10.1.10.2", true)
);
final List<Pair<String, String>> stickinessParameters = Arrays.asList(
new Pair<>("cookie-name", "SESSION_ID")
);
List<LbStickinessPolicy> stickinessPolicies = Arrays.asList(
new LbStickinessPolicy(LbStickinessMethod.StickinessMethodType.AppCookieBased.getName(), stickinessParameters)
);
LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, dests, stickinessPolicies);
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", true);
String result = genConfig(hpg, cmd);
assertThat("http cookie should result in 'mode http' in the resulting haproxy config", result, containsString("mode http"));
assertThat("healthcheck enabled, but result in 'option httpchk' in the resulting haproxy config", result, containsString("appsession SESSION_ID"));


cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true);
stickinessPolicies.set(0, new LbStickinessPolicy(LbStickinessMethod.StickinessMethodType.AppCookieBased.getName(), stickinessParameters, true));
lba[0] = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, dests, stickinessPolicies);
result = genConfig(hpg, cmd);
assertThat("revoked healthcheck should not result in 'option httpchk' in the resulting haproxy config", result, not(containsString("appsession SESSION_ID")));
assertThat("keepalive enabled should not result in 'mode http' in the resulting haproxy config", result, not(containsString("mode http")));

lba[0] = new LoadBalancerTO("1", "10.2.0.1", 80, "http", "bla", false, false, false, dests, null);
result = genConfig(hpg, cmd);
assertThat("revoked healthcheck should not result in 'option httpchk' in the resulting haproxy config", result, not(containsString("appsession SESSION_ID")));
assertThat("keepalive enabled should not result in 'mode http' in the resulting haproxy config", result, not(containsString("mode http")));
}

/**
* Test method for {@link com.cloud.network.HAProxyConfigurator#generateConfiguration(com.cloud.agent.api.routing.LoadBalancerConfigCommand)}.
*/
Expand Down
2 changes: 2 additions & 0 deletions engine/schema/src/com/cloud/network/dao/LoadBalancerDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public interface LoadBalancerDao extends GenericDao<LoadBalancerVO, Long> {

List<LoadBalancerVO> listByIpAddress(long ipAddressId);

long countActiveByIpAddress(long ipAddressId);

List<LoadBalancerVO> listByNetworkIdAndScheme(long networkId, Scheme scheme);

List<LoadBalancerVO> listInTransitionStateByNetworkIdAndScheme(long networkId, Scheme scheme);
Expand Down
Loading

0 comments on commit b88ec4b

Please sign in to comment.