package org.openstack.atlas.service.domain.services.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.exception.LockAcquisitionException;
import org.openstack.atlas.docs.loadbalancers.api.v1.ProtocolPortBindings;
import org.openstack.atlas.service.domain.cache.AtlasCache;
import org.openstack.atlas.service.domain.deadlock.DeadLockRetry;
import org.openstack.atlas.service.domain.entities.*;
import org.openstack.atlas.service.domain.exceptions.*;
import org.openstack.atlas.service.domain.pojos.AccountBilling;
import org.openstack.atlas.service.domain.pojos.ExtendedAccountLoadBalancer;
import org.openstack.atlas.service.domain.pojos.LbQueryStatus;
import org.openstack.atlas.service.domain.services.*;
import org.openstack.atlas.service.domain.services.helpers.AlertType;
import org.openstack.atlas.service.domain.services.helpers.NodesHelper;
import org.openstack.atlas.service.domain.services.helpers.NodesPrioritiesContainer;
import org.openstack.atlas.service.domain.services.helpers.StringHelper;
import org.openstack.atlas.service.domain.usage.BitTag;
import org.openstack.atlas.service.domain.usage.BitTags;
import org.openstack.atlas.service.domain.util.CacheKeyGen;
import org.openstack.atlas.service.domain.util.Constants;
import org.openstack.atlas.service.domain.util.StringUtilities;
import org.openstack.atlas.util.ip.exception.IPStringConversionException;
import org.openstack.atlas.util.ip.exception.IpTypeMissMatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import static org.openstack.atlas.service.domain.entities.LoadBalancerProtocol.*;
import static org.openstack.atlas.service.domain.entities.LoadBalancerStatus.BUILD;
import static org.openstack.atlas.service.domain.entities.LoadBalancerStatus.DELETED;
import static org.openstack.atlas.service.domain.entities.SessionPersistence.*;
@Service
public class LoadBalancerServiceImpl extends BaseService implements LoadBalancerService {
private final Log LOG = LogFactory.getLog(LoadBalancerServiceImpl.class);
@Autowired
private NotificationService notificationService;
@Autowired
private AccountLimitService accountLimitService;
@Autowired
private VirtualIpService virtualIpService;
@Autowired
private HostService hostService;
@Autowired
private NodeService nodeService;
@Autowired
private LoadBalancerStatusHistoryService loadBalancerStatusHistoryService;
@Autowired
private AtlasCache atlasCache;
@Override
@Transactional
public String getErrorPage(Integer lid, Integer aid) throws EntityNotFoundException {
return loadBalancerRepository.getErrorPage(lid, aid);
}
@Override
@Transactional
public String getDefaultErrorPage() throws EntityNotFoundException {
Defaults defaultPage = loadBalancerRepository.getDefaultErrorPage();
if (defaultPage == null) {
throw new EntityNotFoundException("The default error page could not be located.");
}
return defaultPage.getValue();
}
@Override
@DeadLockRetry
@Transactional
public LoadBalancer create(LoadBalancer lb) throws Exception {
if (isLoadBalancerLimitReached(lb.getAccountId())) {
LOG.error("Load balancer limit reached. Sending error response to client...");
throw new LimitReachedException(String.format("Load balancer limit reached. "
+ "Limit is set to '%d'. Contact support if you would like to increase your limit.",
getLoadBalancerLimit(lb.getAccountId())));
}
// If user wants secondary nodes they must have some kind of healthmonitoring
NodesPrioritiesContainer npc = new NodesPrioritiesContainer(lb.getNodes());
if (lb.getHealthMonitor() == null && npc.hasSecondary()) {
throw new BadRequestException(Constants.NoMonitorForSecNodes);
}
// HTTPS Redirect is only valid for HTTPS LBs or LBs with SSL Termination
if (lb.isHttpsRedirect() != null && lb.isHttpsRedirect()) {
if (!lb.getProtocol().equals(LoadBalancerProtocol.HTTPS)) {
throw new BadRequestException("HTTPS Redirect is only valid for load balancers using the HTTPS protocol, " +
"or for load balancers with a 'Secure Only' SSL Termination.");
} else if (lb.getPort() != null && lb.getPort() != 443) { //We just redirect to https://original.url.com which goes to 443
throw new BadRequestException("HTTPS Redirect can only be enabled for HTTPS load balancers using port 443.");
}
}
try {
// Check for black-listed nodes
Node badNode = blackListedItemNode(lb.getNodes());
if (badNode != null) {
throw new BadRequestException(String.format("Invalid node address. The address '%s' is currently not accepted for this request.", badNode.getIpAddress()));
}
} catch (IPStringConversionException ipe) {
LOG.warn("IPStringConversionException thrown. Sending error response to client...");
throw new BadRequestException("IP address was not converted properly, we are unable to process this request.");
} catch (IpTypeMissMatchException ipte) {
LOG.warn("EntityNotFoundException thrown. Sending error response to client...");
throw new BadRequestException("IP addresses type are mismatched, we are unable to process this request.");
}
if (nodeService.detectDuplicateNodes(new LoadBalancer(), lb)) {
throw new BadRequestException("Duplicate nodes detected. Please provide a list of unique node addresses.");
}
if (isNodeLimitReached(lb)) {
throw new LimitReachedException(String.format("Node limit for this load balancer exceeded."));
}
try {
// Check for TCP protocol and port before adding default, since TCP protocol has no default
verifyTCPUDPProtocolandPort(lb);
addDefaultValues(lb);
verifySessionPersistence(lb);
verifyProtocolAndHealthMonitorType(lb);
verifyHalfCloseSupport(lb);
verifyContentCaching(lb);
setHostForNewLoadBalancer(lb);
setVipConfigForLoadBalancer(lb);
} catch (UniqueLbPortViolationException e) {
LOG.warn("The port of the new LB is the same as the LB to which you wish to share a virtual ip.");
throw e;
} catch (AccountMismatchException e) {
LOG.warn("The accounts do not match for the requested shared virtual ip.");
throw e;
} catch (BadRequestException e) {
LOG.debug(e.getMessage());
throw e;
} catch (ProtocolHealthMonitorMismatchException e) {
LOG.warn("Protocol type of HTTP/HTTPS must match Health Monitor Type of HTTP/HTTPS.");
throw e;
} catch (TCPProtocolUnknownPortException e) {
LOG.warn("Port must be supplied for TCP Protocol.");
throw e;
} catch (UnprocessableEntityException e) {
LOG.warn("There is an error regarding the virtual IP hosts, with a shared virtual IP the LoadBalancers must reside within the same cluster.");
throw e;
} catch (OutOfVipsException e) {
LOG.warn("Out of virtual ips! Sending error response to client...");
notificationService.saveAlert(lb.getAccountId(), lb.getId(), e, AlertType.API_FAILURE.name(), e.getMessage());
throw e;
} catch (NoAvailableClusterException e) {
LOG.warn("No available cluster! Sending error response to client...");
notificationService.saveAlert(lb.getAccountId(), lb.getId(), e, AlertType.API_FAILURE.name(), e.getMessage());
throw e;
} catch (IllegalArgumentException e) {
LOG.warn("Virtual Ip could not be processed....");
notificationService.saveAlert(lb.getAccountId(), lb.getId(), e, AlertType.API_FAILURE.name(), e.getMessage());
throw e;
}
LoadBalancer dbLoadBalancer = loadBalancerRepository.create(lb);
dbLoadBalancer.setUserName(lb.getUserName());
joinIpv6OnLoadBalancer(dbLoadBalancer);
// Save history record
loadBalancerStatusHistoryService.save(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.BUILD);
return dbLoadBalancer;
}
@Override
@Transactional
public void setStatus(Integer accoundId, Integer loadbalancerId, LoadBalancerStatus status) throws EntityNotFoundException {
loadBalancerRepository.setStatus(accoundId, loadbalancerId, status);
}
@Override
@Transactional
public boolean testAndSetStatusPending(Integer accountId, Integer loadbalancerId) throws EntityNotFoundException, UnprocessableEntityException {
return loadBalancerRepository.testAndSetStatus(accountId, loadbalancerId, LoadBalancerStatus.PENDING_UPDATE, false);
}
@Override
@DeadLockRetry
@Transactional
public boolean testAndSetStatus(Integer accountId, Integer loadbalancerId, LoadBalancerStatus loadBalancerStatus) throws EntityNotFoundException, UnprocessableEntityException {
boolean isStatusSet;
isStatusSet = loadBalancerRepository.testAndSetStatus(accountId, loadbalancerId, loadBalancerStatus, false);
if (isStatusSet) {
loadBalancerStatusHistoryService.save(accountId, loadbalancerId, loadBalancerStatus);
return isStatusSet;
}
return isStatusSet;
}
@Override
@Transactional(rollbackFor = {Exception.class})
public LoadBalancer prepareForUpdate(LoadBalancer loadBalancer) throws Exception {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(loadBalancer.getId(), loadBalancer.getAccountId());
if (dbLoadBalancer.hasSsl()) {
LOG.debug("Verifying protocol, cannot update protocol while using ssl termination...");
if (loadBalancer.getProtocol() != null && loadBalancer.getProtocol() != dbLoadBalancer.getProtocol()) {
throw new BadRequestException("Cannot update protocol on a load balancer with ssl termination.");
}
}
LOG.info("Performing SSL verifications.");
if (dbLoadBalancer.hasSsl()) {
LOG.info("Verifying unique ports against SSL termination securePort.");
SslTermination ssl = dbLoadBalancer.getSslTermination();
if (loadBalancer.getPort() != null && loadBalancer.getPort() == ssl.getSecurePort()) {
LOG.error("Cannot update load balancer port as it is currently in use by ssl termination.");
throw new BadRequestException(String.format("Port currently assigned to SSL termination for this load balancer. Please try another port."));
} else if (loadBalancer.getPort() != null) {
LOG.info(String.format("Load balancer port:%d and SSL Termination port:%d are unique, continue...", loadBalancer.getPort(), ssl.getSecurePort()));
} else {
LOG.info(String.format("Load balancer port:%d and SSL Termination port:%d are unique, continue...", dbLoadBalancer.getPort(), ssl.getSecurePort()));
}
//Validation for HTTPS redirect
LOG.info("Verifying HTTPS redirect status.");
if ((loadBalancer.isHttpsRedirect() != null && loadBalancer.isHttpsRedirect()) ||
(loadBalancer.isHttpsRedirect() == null && dbLoadBalancer.isHttpsRedirect() != null && dbLoadBalancer.isHttpsRedirect())) {
if (!ssl.isSecureTrafficOnly()) {
LOG.error("Cannot use HTTPS Redirect on a load balancer with a mixed-mode SSL termination.");
throw new BadRequestException("HTTPS Redirect is only valid for load balancers using the HTTPS protocol, " +
"or for load balancers with a 'Secure Only' SSL Termination.");
} else if (ssl.getSecurePort() != 443) { //This would be a ridiculous configuration
LOG.error("HTTPS Redirect can only be enabled for load balancers with SSL Termination using secure port 443.");
throw new BadRequestException("HTTPS Redirect can only be enabled for load balancers with SSL Termination using secure port 443.");
} else if (loadBalancer.getPort() != null && loadBalancer.getPort() != 80) { //dbLoadbalancer doesn't matter, we just can't let them change the port manually after enabling HTTPS-R
LOG.error("Cannot change port on load balancers with HTTPS Redirect enabled.");
throw new BadRequestException("Cannot change port on load balancers with HTTPS Redirect enabled.");
}
}
} else {
if ((loadBalancer.isHttpsRedirect() != null && loadBalancer.isHttpsRedirect()) ||
(loadBalancer.isHttpsRedirect() == null && dbLoadBalancer.isHttpsRedirect() != null && dbLoadBalancer.isHttpsRedirect())) {
if ((loadBalancer.getProtocol() != null && !loadBalancer.getProtocol().equals(LoadBalancerProtocol.HTTPS))
|| loadBalancer.getProtocol() == null && !dbLoadBalancer.getProtocol().equals(LoadBalancerProtocol.HTTPS)) {
LOG.error("HTTPS Redirect can only be enabled for HTTPS or SSL load balancers.");
throw new BadRequestException("HTTPS Redirect is only valid for load balancers using the HTTPS protocol, " +
"or for load balancers with a 'Secure Only' SSL Termination.");
} else if ((loadBalancer.getPort() != null && loadBalancer.getPort() != 443)
|| (loadBalancer.getPort() == null && dbLoadBalancer.getPort() != 443)) {
//We just redirect to https://original.url.com which goes to 443
LOG.error("HTTPS Redirect can only be enabled for HTTPS load balancers using port 443.");
throw new BadRequestException("HTTPS Redirect can only be enabled for HTTPS load balancers using port 443.");
}
}
}
LOG.debug("Updating the lb status to pending_update");
if (!testAndSetStatus(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_UPDATE)) {
String message = StringHelper.immutableLoadBalancer(dbLoadBalancer);
LOG.warn(message);
throw new ImmutableEntityException(message);
}
if (loadBalancer.getPort() != null && !loadBalancer.getPort().equals(dbLoadBalancer.getPort())) {
LOG.debug("Updating loadbalancer port to " + loadBalancer.getPort());
if (loadBalancerRepository.canUpdateToNewPort(loadBalancer.getPort(), dbLoadBalancer)) {
loadBalancerRepository.updatePortInJoinTable(loadBalancer);
dbLoadBalancer.setPort(loadBalancer.getPort());
} else {
LOG.error("Cannot update load balancer port as it is currently in use by another virtual ip and could be in conflict with the load balancer protocol.");
throw new BadRequestException(String.format("Port currently assigned to one of the virtual ips. Please verify protocol/port combinations."));
}
}
if (loadBalancer.getName() != null && !loadBalancer.getName().equals(dbLoadBalancer.getName())) {
LOG.debug("Updating loadbalancer name to " + loadBalancer.getName());
dbLoadBalancer.setName(loadBalancer.getName());
}
if (loadBalancer.getAlgorithm() != null && !loadBalancer.getAlgorithm().equals(dbLoadBalancer.getAlgorithm())) {
LOG.debug("Updating loadbalancer algorithm to " + loadBalancer.getAlgorithm());
dbLoadBalancer.setAlgorithm(loadBalancer.getAlgorithm());
}
if (loadBalancer.getTimeout() != null && !loadBalancer.getTimeout().equals(dbLoadBalancer.getTimeout())) {
LOG.debug("Updating loadbalancer timeout to " + loadBalancer.getTimeout());
dbLoadBalancer.setTimeout(loadBalancer.getTimeout());
}
if (loadBalancer.getProtocol() != null && !loadBalancer.getProtocol().equals(dbLoadBalancer.getProtocol())) {
verifyTCPUDPProtocolandPort(loadBalancer, dbLoadBalancer);
if (loadBalancer.isHalfClosed() != null) {
verifyHalfCloseSupport(loadBalancer);
} else {
verifyHalfCloseSupport(loadBalancer, dbLoadBalancer.isHalfClosed());
}
boolean isValidProto = true;
if (checkLBProtocol(loadBalancer)) {
//Move
if (loadBalancer.getPort() != null) {
dbLoadBalancer.setPort(loadBalancer.getPort());
} else {
dbLoadBalancer.setPort(ProtocolPortBindings.getPortByKey(loadBalancer.getProtocol().toString()));
}
for (LoadBalancerJoinVip vip : dbLoadBalancer.getLoadBalancerJoinVipSet()) {
List<LoadBalancer> sharedLbs = virtualIpRepository.getLoadBalancersByVipId(vip.getVirtualIp().getId());
List<LoadBalancer> sharedLbsToCheck = new ArrayList<LoadBalancer>();
for (LoadBalancer lb : sharedLbs) {
if (!lb.getId().equals(loadBalancer.getId())) {
sharedLbsToCheck.add(lb);
}
}
isValidProto = verifySharedVipProtocols(sharedLbsToCheck, loadBalancer);
}
for (LoadBalancerJoinVip6 vip6 : dbLoadBalancer.getLoadBalancerJoinVip6Set()) {
List<LoadBalancer> sharedLbs = virtualIpv6Repository.getLoadBalancersByVipId(vip6.getVirtualIp().getId());
List<LoadBalancer> sharedLbsToCheck = new ArrayList<LoadBalancer>();
for (LoadBalancer lb : sharedLbs) {
if (!lb.getId().equals(loadBalancer.getId())) {
sharedLbsToCheck.add(lb);
}
}
isValidProto = verifySharedVipProtocols(sharedLbsToCheck, loadBalancer);
}
}
if (!isValidProto) {
throw new BadRequestException("Protocol is not valid. Please verify shared virtual ips and the ports being shared.");
}
//check for health monitor type and allow update only if protocol matches health monitory type for HTTP and HTTPS
boolean portHMTypecheck = true;
if (dbLoadBalancer.getHealthMonitor() != null) {
if (dbLoadBalancer.getHealthMonitor().getType() != null) {
if (dbLoadBalancer.getHealthMonitor().getType().name().equals(LoadBalancerProtocol.HTTP.name())) {
//incoming port not HTTP
if (!(loadBalancer.getProtocol().name().equals(LoadBalancerProtocol.HTTP.name()))) {
portHMTypecheck = false;
}
} else if (dbLoadBalancer.getHealthMonitor().getType().name().equals(LoadBalancerProtocol.HTTPS.name())) {
//incoming port not HTTP
if (!(loadBalancer.getProtocol().name().equals(LoadBalancerProtocol.HTTPS.name()))) {
portHMTypecheck = false;
}
}
}
}
if (portHMTypecheck) {
LoadBalancerProtocol lbProtocol = loadBalancer.getProtocol();
SessionPersistence dbPersistenceType = dbLoadBalancer.getSessionPersistence();
if (lbProtocol.equals(HTTP)) {
if ((dbPersistenceType == SessionPersistence.HTTP_COOKIE)) {
LOG.debug("Existing session persistence valid with HTTP protocol. Updating loadbalancer protocol to " + lbProtocol);
dbLoadBalancer.setProtocol(lbProtocol);
} else {
LOG.debug("Cannot have HTTP protocol with " + dbPersistenceType + " persistence. Updating loadbalancer protocol, but disabling session persistence.");
dbLoadBalancer.setSessionPersistence(SessionPersistence.NONE);
dbLoadBalancer.setProtocol(lbProtocol);
}
} else if (lbProtocol.equals(HTTPS)) {
if ((dbPersistenceType == SessionPersistence.SSL_ID)) {
LOG.debug("Existing session persistence valid with HTTPS protocol. Updating loadbalancer protocol to " + lbProtocol);
dbLoadBalancer.setProtocol(lbProtocol);
} else {
LOG.debug("Cannot have HTTPS protocol with " + dbPersistenceType + " persistence. Updating loadbalancer protocol, but disabling session persistence.");
dbLoadBalancer.setSessionPersistence(SessionPersistence.NONE);
dbLoadBalancer.setProtocol(lbProtocol);
}
} else {
dbLoadBalancer.setContentCaching(false);
if ((dbPersistenceType == SessionPersistence.SOURCE_IP)) {
LOG.debug("Existing session persistence valid with " + lbProtocol + " protocol. Updating loadbalancer protocol to " + lbProtocol);
dbLoadBalancer.setProtocol(lbProtocol);
} else {
LOG.debug("Cannot have " + lbProtocol + " + protocol with " + dbPersistenceType + " persistence. Updating loadbalancer protocol, but disabling session persistence.");
dbLoadBalancer.setSessionPersistence(SessionPersistence.NONE);
dbLoadBalancer.setProtocol(lbProtocol);
}
}
} else {
LOG.error("Cannot update port as the loadbalancer has a incompatible Health Monitor type");
throw new BadRequestException(String.format("Cannot update port as the loadbalancer has a incompatible Health Monitor type"));
}
}
LOG.debug(String.format("Verifying connectionLogging and contentCaching... if enabled, they are valid only with HTTP protocol.."));
verifyProtocolLoggingAndCaching(loadBalancer, dbLoadBalancer);
LOG.debug("Update half close support in load balancer service impl");
if (loadBalancer.isHalfClosed() != null) {
verifyHalfCloseSupport(dbLoadBalancer, loadBalancer.isHalfClosed());
dbLoadBalancer.setHalfClosed(loadBalancer.isHalfClosed());
}
LOG.debug("Updating loadbalancer httpsRedirect to " + loadBalancer.isHttpsRedirect());
if (loadBalancer.isHttpsRedirect() != null) {
dbLoadBalancer.setHttpsRedirect(loadBalancer.isHttpsRedirect());
}
dbLoadBalancer = loadBalancerRepository.update(dbLoadBalancer);
dbLoadBalancer.setUserName(loadBalancer.getUserName());
LOG.debug("Updated the loadbalancer in DB. Now sending response back.");
return dbLoadBalancer;
}
private void verifyProtocolLoggingAndCaching(LoadBalancer loadBalancer, LoadBalancer dbLoadBalancer) throws UnprocessableEntityException {
String logErr = "Protocol must be HTTP for connection logging.";
String ccErr = "Protocol must be HTTP for content caching.";
String enable = " is Being enabled on the loadbalancer";
String disable = " is Being disabled on the loadbalancer";
if (loadBalancer.isConnectionLogging() != null && !loadBalancer.isConnectionLogging().equals(dbLoadBalancer.isConnectionLogging())) {
if (loadBalancer.isConnectionLogging()) {
if (loadBalancer.getProtocol() != LoadBalancerProtocol.HTTP) {
LOG.error(logErr);
throw new UnprocessableEntityException(logErr);
}
LOG.debug("ConnectionLogging" + enable);
} else {
LOG.debug("ConnectionLogging" + disable);
}
dbLoadBalancer.setConnectionLogging(loadBalancer.isConnectionLogging());
}
if (loadBalancer.isContentCaching() != null && !loadBalancer.isContentCaching().equals(dbLoadBalancer.isConnectionLogging())) {
if (loadBalancer.isContentCaching()) {
if (loadBalancer.getProtocol() != LoadBalancerProtocol.HTTP) {
LOG.error(ccErr);
throw new UnprocessableEntityException(ccErr);
}
LOG.debug("ContentCaching" + enable);
} else {
LOG.debug("ContentCaching" + disable);
}
dbLoadBalancer.setConnectionLogging(loadBalancer.isConnectionLogging());
}
}
@Transactional
public UserPages getUserPages(Integer id, Integer accountId) throws EntityNotFoundException {
LoadBalancer dLb = loadBalancerRepository.getByIdAndAccountId(id, accountId);
UserPages up = dLb.getUserPages();
return up;
}
@Override
@Transactional
public LoadBalancer get(Integer id, Integer accountId) throws EntityNotFoundException {
return loadBalancerRepository.getByIdAndAccountId(id, accountId);
}
@Override
@Transactional
public LoadBalancer getWithUserPages(Integer id, Integer accountId) throws EntityNotFoundException {
return loadBalancerRepository.getByIdAndAccountIdWithUserPages(id, accountId);
}
@Override
public List<org.openstack.atlas.service.domain.pojos.AccountLoadBalancer> getAccountLoadBalancers(Integer accountId) {
return loadBalancerRepository.getAccountLoadBalancers(accountId);
}
@Override
public List<ExtendedAccountLoadBalancer> getExtendedAccountLoadBalancer(Integer accountId) {
String key = CacheKeyGen.generateKeyName(accountId);
ArrayList<ExtendedAccountLoadBalancer> extendedAccountLoadBalancers;
boolean b = atlasCache.containsKey(key);
extendedAccountLoadBalancers = (ArrayList<ExtendedAccountLoadBalancer>) atlasCache.get(key);
if (extendedAccountLoadBalancers == null) {
LOG.debug("Setting ExtendedLoadBalancers in cache for: " + " at " + Calendar.getInstance().getTime().toString());
extendedAccountLoadBalancers = loadBalancerRepository.getExtendedAccountLoadBalancers(accountId);
atlasCache.set(key, extendedAccountLoadBalancers);
} else {
LOG.debug("Retrieved ExtendedLoadBalancers from cache for: " + accountId + " at " + Calendar.getInstance().getTime().toString());
return extendedAccountLoadBalancers;
}
return extendedAccountLoadBalancers;
}
@Override
@Transactional
public Suspension createSuspension(LoadBalancer loadBalancer, Suspension suspension) {
return loadBalancerRepository.createSuspension(loadBalancer, suspension);
}
@Override
@Transactional
public void removeSuspension(int loadbalancerId) {
loadBalancerRepository.removeSuspension(loadbalancerId);
}
@Override
public LoadBalancer get(Integer id) throws EntityNotFoundException {
return loadBalancerRepository.getById(id);
}
@Override
@Transactional
public LoadBalancer update(LoadBalancer lb) throws Exception {
return loadBalancerRepository.update(lb);
}
@Override
public AccountBilling getAccountBilling(Integer accountId, Calendar startTime, Calendar endTime) throws EntityNotFoundException {
return loadBalancerRepository.getAccountBilling(accountId, startTime, endTime);
}
@Override
public List<LoadBalancer> getLoadbalancersGeneric(Integer accountId,
String status, LbQueryStatus qs, Calendar changedCal,
Integer offset, Integer limit, Integer marker) throws BadRequestException {
return loadBalancerRepository.getLoadbalancersGeneric(accountId, status, qs, changedCal, offset, limit, marker);
}
@Override
@Transactional
public void updateLoadBalancers(List<LoadBalancer> lbs) throws Exception {
LOG.debug("Updating load balancers in database...");
for (LoadBalancer lb : lbs) {
LoadBalancer dbLb = get(lb.getId());
if (lb.getHost() != null) {
dbLb.setHost(lb.getHost());
}
dbLb.setStatus(LoadBalancerStatus.ACTIVE);
update(dbLb);
}
LOG.debug("Successfully updated load balancers in database...");
}
@Override
@Transactional
public void setLoadBalancerAttrs(LoadBalancer lb) throws EntityNotFoundException {
loadBalancerRepository.setLoadBalancerAttrs(lb);
}
@Override
@Transactional
public LoadBalancer prepareMgmtLoadBalancerDeletion(LoadBalancer loadBalancer, LoadBalancerStatus statusToCheck) throws EntityNotFoundException, UnprocessableEntityException {
LOG.debug("Entering " + getClass());
LoadBalancer dbLb = null;
LOG.debug(String.format("%s del msgLB[%d]\n", loadBalancer.getId(), loadBalancer.getId()));
dbLb = loadBalancerRepository.getById(loadBalancer.getId());
//this operation only allows for loadbalancers to be deleted that are in ERROR status SITESLB-795
if (!(dbLb.getStatus().equals(statusToCheck))) {
String msg = String.format("%s msgLB[%d] dbLb[%d] status is %s and cannot be deleted. ", loadBalancer.getId(), loadBalancer.getId(), dbLb.getId(), dbLb.getStatus().toString());
LOG.warn(msg);
throw new UnprocessableEntityException(msg);
}
//this use case requires a loadbalancer in ERROR or SUSPENDED status to be deleted from Zeus and set to deleted in DB
LOG.debug(String.format("Updating dbLB[%d] status to pending_delete", dbLb.getId()));
dbLb.setStatus(LoadBalancerStatus.PENDING_DELETE);
dbLb = loadBalancerRepository.update(dbLb);
dbLb.setUserName(loadBalancer.getUserName());
// Add atom entry
// String atomTitle = "Load Balancer in pending delete status";
// String atomSummary = "Load balancer in pending delete status";
// notificationService.saveLoadBalancerEvent(loadBalancer.getUserName(), loadBalancer.getAccountId(), loadBalancer.getId(), atomTitle, atomSummary, PENDING_DELETE_LOADBALANCER, DELETE, INFO);
LOG.debug("Leaving " + getClass());
return dbLb;
}
@Override
public List<LoadBalancer> getLoadBalancersForAudit(String status, Calendar changedSince) throws Exception {
LoadBalancerStatus error = null;
LoadBalancerStatus build = null;
LoadBalancerStatus pending_update = null;
LoadBalancerStatus pending_delete = null;
String[] statues = status.split("\\,");
int statuesLength = statues.length;
//map the values
for (String stat : statues) {
if (stat.equals("error")) {
error = LoadBalancerStatus.ERROR;
}
if (stat.equals("build")) {
build = LoadBalancerStatus.BUILD;
}
if (stat.equals("pending_update")) {
pending_update = LoadBalancerStatus.PENDING_UPDATE;
}
if (stat.equals("pending_delete")) {
pending_delete = LoadBalancerStatus.PENDING_DELETE;
}
}
return loadBalancerRepository.getLoadBalancersStatusAndDate(error, build, pending_update, pending_delete, changedSince);
}
@Override
@Transactional(rollbackFor = {EntityNotFoundException.class, ImmutableEntityException.class, UnprocessableEntityException.class, BadRequestException.class})
public List<LoadBalancer> prepareForDelete(Integer accountId, List<Integer> loadBalancerIds) throws BadRequestException {
List<Integer> badLbIds = new ArrayList<Integer>();
List<Integer> badLbStatusIds = new ArrayList<Integer>();
List<LoadBalancer> loadBalancers = new ArrayList<LoadBalancer>();
for (int lbIdToDelete : loadBalancerIds) {
try {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(lbIdToDelete, accountId);
if (!loadBalancerRepository.testAndSetStatus(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_DELETE, false)) {
LOG.warn(StringHelper.immutableLoadBalancer(dbLoadBalancer));
badLbStatusIds.add(lbIdToDelete);
} else {
//Set status record
loadBalancerStatusHistoryService.save(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_DELETE);
// // Add atom entry
// String atomTitle = "Load Balancer in pending delete status";
// String atomSummary = "Load balancer in pending delete status";
// notificationService.saveLoadBalancerEvent(dbLoadBalancer.getUserName(), dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), atomTitle, atomSummary, PENDING_DELETE_LOADBALANCER, DELETE, INFO);
}
loadBalancers.add(dbLoadBalancer);
} catch (Exception e) {
badLbIds.add(lbIdToDelete);
}
}
if (!badLbIds.isEmpty()) {
throw new BadRequestException(String.format("Must provide valid load balancers: %s could not be found.", StringUtilities.DelimitString(badLbIds, ",")));
}
if (!badLbStatusIds.isEmpty()) {
throw new BadRequestException(String.format("Must provide valid load balancers: %s are immutable and could not be processed.", StringUtilities.DelimitString(badLbStatusIds, ",")));
}
return loadBalancers;
}
@Override
@Transactional
public void prepareForDelete(LoadBalancer lb) throws Exception {
List<Integer> loadBalancerIds = new ArrayList<Integer>();
loadBalancerIds.add(lb.getId());
prepareForDelete(lb.getAccountId(), loadBalancerIds);
}
@Override
@DeadLockRetry
@Transactional
public LoadBalancer pseudoDelete(LoadBalancer lb) throws Exception {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(lb.getId(), lb.getAccountId());
//Remove error page
loadBalancerRepository.removeErrorPage(dbLoadBalancer.getId(), dbLoadBalancer.getAccountId());
dbLoadBalancer.setStatus(DELETED);
dbLoadBalancer = loadBalancerRepository.update(dbLoadBalancer);
virtualIpService.removeAllVipsFromLoadBalancer(dbLoadBalancer);
return dbLoadBalancer;
}
@Override
public Boolean isLoadBalancerLimitReached(Integer accountId) {
Boolean limitReached = false;
try {
LOG.debug(String.format("Obtaining load balancer limit for account '%d' from database...", accountId));
Integer limit = accountLimitService.getLimit(accountId, AccountLimitType.LOADBALANCER_LIMIT);
final Integer numNonDeletedLoadBalancers = loadBalancerRepository.getNumNonDeletedLoadBalancersForAccount(accountId);
limitReached = (numNonDeletedLoadBalancers >= limit);
} catch (EntityNotFoundException e) {
LOG.error(String.format("No loadbalancer limit found. "
+ "Customer with account '%d' could potentially be creating too many loadbalancers! "
+ "Allowing operation to continue...", accountId), e);
notificationService.saveAlert(accountId, null, e, AlertType.DATABASE_FAILURE.name(), "No loadbalancer limit found");
}
return limitReached;
}
public Boolean isNodeLimitReached(LoadBalancer loadBalancer) {
try {
LOG.debug(String.format("Obtaining node limit for acount '%d' from database...", loadBalancer.getAccountId()));
Integer limit = accountLimitService.getLimit(loadBalancer.getAccountId(), AccountLimitType.NODE_LIMIT);
if (loadBalancer.getNodes().size() > limit) {
return true;
}
} catch (EntityNotFoundException e) {
LOG.error(String.format("No node limit found. "
+ "Customer with account '%d' could potentially be creating too many nodes! "
+ "Allowing operation to continue...", loadBalancer.getAccountId()), e);
}
return false;
}
@Override
public Integer getLoadBalancerLimit(Integer accountId) throws EntityNotFoundException {
return accountLimitService.getLimit(accountId, AccountLimitType.LOADBALANCER_LIMIT);
}
@Override
@Transactional
public void setStatus(LoadBalancer lb, LoadBalancerStatus status) {
try {
loadBalancerRepository.setStatus(lb, status);
LoadBalancer dbLb = loadBalancerRepository.getById(lb.getId());
loadBalancerStatusHistoryService.save(dbLb.getAccountId(), dbLb.getId(), status);
} catch (EntityNotFoundException e) {
LOG.warn(String.format("Cannot set status for loadbalancer '%d' as it does not exist.", lb.getId()));
}
}
@Override
@Transactional
public void setStatusForOp(LoadBalancer lb, LoadBalancerStatus status) throws EntityNotFoundException {
loadBalancerRepository.setStatusForOp(lb.getAccountId(), lb.getId(), status);
}
@Override
public void addDefaultValues(LoadBalancer loadBalancer) {
final Integer TIMEOUT_DEFAULT = 30;
loadBalancer.setStatus(BUILD);
NodesHelper.setNodesToStatus(loadBalancer, NodeStatus.ONLINE);
if (loadBalancer.getAlgorithm() == null) {
loadBalancer.setAlgorithm(LoadBalancerAlgorithm.RANDOM);
}
if (loadBalancer.isConnectionLogging() == null) {
loadBalancer.setConnectionLogging(false);
}
if ((loadBalancer.getProtocol() == null && loadBalancer.getPort() == null) || (loadBalancer.getProtocol() == null && loadBalancer.getPort() != null)) {
LoadBalancerProtocolObject defaultProtocol = loadBalancerRepository.getDefaultProtocol();
loadBalancer.setProtocol(defaultProtocol.getName());
loadBalancer.setPort(defaultProtocol.getPort());
} else if (loadBalancer.getProtocol() != null && loadBalancer.getPort() == null) {
LoadBalancerProtocolObject protocol = loadBalancerRepository.getProtocol(loadBalancer.getProtocol());
loadBalancer.setPort(protocol.getPort());
}
if (loadBalancer.getSessionPersistence() == null) {
loadBalancer.setSessionPersistence(SessionPersistence.NONE);
}
for (Node node : loadBalancer.getNodes()) {
if (node.getWeight() == null) {
node.setWeight(Constants.DEFAULT_NODE_WEIGHT);
}
}
if (loadBalancer.getTimeout() == null) {
loadBalancer.setTimeout(TIMEOUT_DEFAULT);
}
if (loadBalancer.isHalfClosed() == null) {
loadBalancer.setHalfClosed(false);
}
if (loadBalancer.isHttpsRedirect() == null) {
loadBalancer.setHttpsRedirect(false);
}
}
private void verifySessionPersistence(LoadBalancer queueLb) throws BadRequestException {
// Duplicated in sessionPersistenceServiceImpl ...
SessionPersistence persistenceType = queueLb.getSessionPersistence();
LoadBalancerProtocol dbProtocol = queueLb.getProtocol();
String httpErrMsg = "HTTP_COOKIE Session persistence is only valid with HTTP protocol with or without SSL termination.";
String sslErrMsg = "SSL_ID session persistence is only valid with the HTTPS protocol. ";
if (persistenceType != NONE) {
if (persistenceType == HTTP_COOKIE && (dbProtocol != HTTP)) {
LOG.info(httpErrMsg);
throw new BadRequestException(httpErrMsg);
}
if (persistenceType == SSL_ID && (dbProtocol != HTTPS)) {
LOG.info(sslErrMsg);
throw new BadRequestException(sslErrMsg);
}
}
}
private void verifyProtocolAndHealthMonitorType(LoadBalancer queueLb) throws ProtocolHealthMonitorMismatchException {
if (queueLb.getHealthMonitor() != null) {
LOG.info("Health Monitor detected. Verifying that the load balancer's protocol matches the monitor type.");
if (queueLb.getProtocol().equals(LoadBalancerProtocol.DNS_UDP) || queueLb.getProtocol().equals(LoadBalancerProtocol.UDP) || queueLb.getProtocol().equals(LoadBalancerProtocol.UDP_STREAM)) {
throw new ProtocolHealthMonitorMismatchException("Protocol UDP, UDP_STREAM and DNS_UDP are not allowed with health monitors. ");
}
if (queueLb.getHealthMonitor().getType() != null) {
if (queueLb.getHealthMonitor().getType().name().equals(HealthMonitorType.HTTP.name())) {
if (!(queueLb.getProtocol().equals(LoadBalancerProtocol.HTTP))) {
throw new ProtocolHealthMonitorMismatchException("Protocol must be HTTP for an HTTP health monitor.");
}
} else if (queueLb.getHealthMonitor().getType().name().equals(HealthMonitorType.HTTPS.name())) {
if (!(queueLb.getProtocol().equals(LoadBalancerProtocol.HTTPS))) {
throw new ProtocolHealthMonitorMismatchException("Protocol must be HTTPS for an HTTPS health monitor.");
}
}
}
}
}
private void verifyContentCaching(LoadBalancer queueLb) throws ProtocolHealthMonitorMismatchException, BadRequestException {
if (queueLb.isContentCaching() != null && queueLb.isContentCaching()) {
if (queueLb.getProtocol() != LoadBalancerProtocol.HTTP) {
throw new BadRequestException("Content caching can only be enabled for HTTP loadbalancers.");
}
} else if (queueLb.isContentCaching() == null) {
queueLb.setContentCaching(false);
}
}
private void verifyTCPUDPProtocolandPort(LoadBalancer queueLb, LoadBalancer dbLb) throws TCPProtocolUnknownPortException {
if (queueLb.getProtocol() != null && (queueLb.getProtocol().equals(LoadBalancerProtocol.TCP) || queueLb.getProtocol().equals(LoadBalancerProtocol.TCP_CLIENT_FIRST)) || (queueLb.getProtocol().equals(LoadBalancerProtocol.UDP) || (queueLb.getProtocol().equals(LoadBalancerProtocol.UDP_STREAM)))) {
LOG.info("TCP and UDP Protocol detected. Port must exists");
if (queueLb.getPort() == null) {
throw new TCPProtocolUnknownPortException("Must provide port for TCP and UDP protocols.");
}
}
}
private void verifyTCPUDPProtocolandPort(LoadBalancer queueLb) throws TCPProtocolUnknownPortException {
verifyTCPUDPProtocolandPort(queueLb, null);
}
private void verifyHalfCloseSupport(LoadBalancer lb, boolean isHalfClose) throws BadRequestException {
if (isHalfClose) {
if (lb.getProtocol() != null && !(lb.getProtocol().equals(LoadBalancerProtocol.TCP) || lb.getProtocol().equals(LoadBalancerProtocol.TCP_CLIENT_FIRST))) {
LOG.debug("TCP or TCP_CLIENT_FIRST Protocol only allowed with Half Close Support and will not be enabled at this time. ");
throw new BadRequestException("Must provide valid protocol for half close support, please view documentation for more details. ", new Exception("Half Close support and Load Balancer protocol not valid. "));
}
LOG.debug("Half Close support will be enabled for load balancer ");
}
}
private void verifyHalfCloseSupport(LoadBalancer lb) throws BadRequestException {
if (lb.isHalfClosed() != null) {
verifyHalfCloseSupport(lb, lb.isHalfClosed());
}
}
private void setHostForNewLoadBalancer(LoadBalancer loadBalancer) throws EntityNotFoundException, UnprocessableEntityException, ClusterStatusException, BadRequestException, NoAvailableClusterException {
boolean isHost = false;
LoadBalancer gLb = new LoadBalancer();
Integer vipId = null;
try {
for (LoadBalancerJoinVip loadBalancerJoinVip : loadBalancer.getLoadBalancerJoinVipSet()) {
if (loadBalancerJoinVip.getVirtualIp().getId() != null) {
isHost = true;
vipId = loadBalancerJoinVip.getVirtualIp().getId();
gLb = virtualIpRepository.getLoadBalancersByVipId(vipId).iterator().next();
}
}
for (LoadBalancerJoinVip6 loadBalancerJoinVip6 : loadBalancer.getLoadBalancerJoinVip6Set()) {
if (loadBalancerJoinVip6.getVirtualIp().getId() != null) {
isHost = true;
vipId = loadBalancerJoinVip6.getVirtualIp().getId();
gLb = virtualIpv6Repository.getLoadBalancersByVipId(vipId).iterator().next();
}
}
} catch (NoSuchElementException nse) {
LOG.info(String.format("Virtual ip id provided was not valid. for Account: %s LoadBalancer %s VIPID: %s", loadBalancer.getAccountId(), loadBalancer.getId(), vipId));
throw new BadRequestException("Shared virtual ip could not be found. Please provide a valid virtual ip id to process this request.");
}
if (!isHost) {
loadBalancer.setHost(hostService.getDefaultActiveHostAndActiveCluster(loadBalancer.getAccountId()));
} else {
if (gLb != null) {
loadBalancer.setHost(gLb.getHost());
}
}
}
private void setVipConfigForLoadBalancer(LoadBalancer lbFromApi) throws OutOfVipsException, AccountMismatchException, UniqueLbPortViolationException, EntityNotFoundException, BadRequestException, ImmutableEntityException, UnprocessableEntityException {
if (!lbFromApi.getLoadBalancerJoinVipSet().isEmpty()) {
if (lbFromApi.getLoadBalancerJoinVipSet().size() > 1) {
throw new BadRequestException("Cannot supply more than one IPV4 virtual ip.");
}
Set<LoadBalancerJoinVip> newVipConfig = new HashSet<LoadBalancerJoinVip>();
List<VirtualIp> vipsOnAccount = virtualIpRepository.getVipsByAccountId(lbFromApi.getAccountId());
for (LoadBalancerJoinVip loadBalancerJoinVip : lbFromApi.getLoadBalancerJoinVipSet()) {
if (loadBalancerJoinVip.getVirtualIp().getId() == null) {
// Add a new vip to set
VirtualIp newVip = virtualIpService.allocateIpv4VirtualIp(loadBalancerJoinVip.getVirtualIp(), lbFromApi.getHost().getCluster());
LoadBalancerJoinVip newJoinRecord = new LoadBalancerJoinVip();
newJoinRecord.setVirtualIp(newVip);
newVipConfig.add(newJoinRecord);
} else {
// Add shared vip to set
newVipConfig.addAll(getSharedIpv4Vips(loadBalancerJoinVip.getVirtualIp(), vipsOnAccount, lbFromApi));
}
}
lbFromApi.setLoadBalancerJoinVipSet(newVipConfig);
}
if (!lbFromApi.getLoadBalancerJoinVip6Set().isEmpty()) {
if (lbFromApi.getLoadBalancerJoinVip6Set().size() > 1) {
throw new BadRequestException("Cannot supply more than one IPV6 virtual ip");
}
Set<LoadBalancerJoinVip6> newVip6Config = new HashSet<LoadBalancerJoinVip6>();
List<VirtualIpv6> vips6OnAccount = virtualIpv6Repository.getVips6ByAccountId(lbFromApi.getAccountId());
Set<LoadBalancerJoinVip6> loadBalancerJoinVip6SetConfig = lbFromApi.getLoadBalancerJoinVip6Set();
lbFromApi.setLoadBalancerJoinVip6Set(null);
for (LoadBalancerJoinVip6 loadBalancerJoinVip6 : loadBalancerJoinVip6SetConfig) {
if (loadBalancerJoinVip6.getVirtualIp().getId() == null) {
VirtualIpv6 ipv6 = virtualIpService.allocateIpv6VirtualIp(lbFromApi);
LoadBalancerJoinVip6 lbjv6 = new LoadBalancerJoinVip6();
lbjv6.setVirtualIp(ipv6);
newVip6Config.add(lbjv6);
} else {
//share ipv6 vip here..
newVip6Config.addAll(getSharedIpv6Vips(loadBalancerJoinVip6.getVirtualIp(), vips6OnAccount, lbFromApi));
}
lbFromApi.setLoadBalancerJoinVip6Set(newVip6Config);
}
}
}
private Set<LoadBalancerJoinVip> getSharedIpv4Vips(VirtualIp vipConfig, List<VirtualIp> vipsOnAccount, LoadBalancer loadBalancer) throws AccountMismatchException, UniqueLbPortViolationException, BadRequestException {
Set<LoadBalancerJoinVip> sharedVips = new HashSet<LoadBalancerJoinVip>();
boolean belongsToProperAccount = false;
String uniqueMsg = "Another load balancer is currently using the requested port with the shared virtual ip.";
// Verify this is a valid virtual ip to share
for (VirtualIp vipOnAccount : vipsOnAccount) {
if (vipOnAccount.getId().equals(vipConfig.getId())) {
if (virtualIpService.isIpv4VipPortCombinationInUse(vipOnAccount, loadBalancer.getPort())) {
if (!checkLBProtocol(loadBalancer)) {
throw new UniqueLbPortViolationException(uniqueMsg);
} else {
if (!verifySharedVipProtocols(vipOnAccount, loadBalancer)) {
throw new BadRequestException("The requesting load balancer is in conflict with the shared virtual ip port/protocol combination. Please refer to documentation for more info.");
}
}
}
belongsToProperAccount = true;
LoadBalancerJoinVip loadBalancerJoinVip = new LoadBalancerJoinVip();
loadBalancerJoinVip.setVirtualIp(vipOnAccount);
sharedVips.add(loadBalancerJoinVip);
}
}
if (!belongsToProperAccount) {
throw new AccountMismatchException("Invalid requesting account for the shared virtual ip.");
}
return sharedVips;
}
private Set<LoadBalancerJoinVip6> getSharedIpv6Vips(VirtualIpv6 vipConfig, List<VirtualIpv6> vipsOnAccount, LoadBalancer loadBalancer) throws AccountMismatchException, UniqueLbPortViolationException, BadRequestException {
Set<LoadBalancerJoinVip6> sharedVips = new HashSet<LoadBalancerJoinVip6>();
boolean belongsToProperAccount = false;
String uniqueMsg = "Another load balancer is currently using the requested port with the shared virtual ip.";
// Verify this is a valid virtual ip to share
for (VirtualIpv6 vipOnAccount : vipsOnAccount) {
if (vipOnAccount.getId().equals(vipConfig.getId())) {
if (virtualIpService.isIpv6VipPortCombinationInUse(vipOnAccount, loadBalancer.getPort())) {
if (!checkLBProtocol(loadBalancer)) {
throw new UniqueLbPortViolationException(uniqueMsg);
} else {
if (!verifySharedVip6Protocols(vipOnAccount, loadBalancer)) {
throw new BadRequestException("The requesting load balancer is in conflict with the shared virtual ip port/protocol combination. Please refer to documentation for more info.");
}
}
}
belongsToProperAccount = true;
LoadBalancerJoinVip6 loadBalancerJoinVip6 = new LoadBalancerJoinVip6();
loadBalancerJoinVip6.setVirtualIp(vipOnAccount);
sharedVips.add(loadBalancerJoinVip6);
}
}
if (!belongsToProperAccount) {
throw new AccountMismatchException("Invalid requesting account for the shared virtual ip.");
}
return sharedVips;
}
private boolean checkLBProtocol(LoadBalancer loadBalancer) {
return loadBalancer.getProtocol() == LoadBalancerProtocol.TCP || loadBalancer.getProtocol() == LoadBalancerProtocol.DNS_TCP
|| loadBalancer.getProtocol() == LoadBalancerProtocol.DNS_UDP || loadBalancer.getProtocol() == LoadBalancerProtocol.UDP || loadBalancer.getProtocol() == LoadBalancerProtocol.UDP_STREAM || loadBalancer.getProtocol() == LoadBalancerProtocol.TCP_CLIENT_FIRST || loadBalancer.getProtocol() == LoadBalancerProtocol.TCP_STREAM;
}
private boolean verifySharedVipProtocols(VirtualIp vip, LoadBalancer loadBalancer) {
return verifySharedVipProtocols(virtualIpService.getLoadBalancerByVipId(vip.getId()), loadBalancer);
}
private boolean verifySharedVip6Protocols(VirtualIpv6 vip6, LoadBalancer loadBalancer) {
return verifySharedVipProtocols(virtualIpv6Repository.getLoadBalancersByVipId(vip6.getId()), loadBalancer);
}
private boolean verifySharedVipProtocols(List<LoadBalancer> sharedLbs, LoadBalancer loadBalancer) {
int invalidProtos = 0;
for (LoadBalancer lb : sharedLbs) {
if (!checkLBProtocol(lb)) {
invalidProtos++;
} else {
if (!verifyProtoGroups(lb, loadBalancer)) {
invalidProtos++;
}
}
}
return invalidProtos < 1;
}
private boolean verifyProtoGroups(LoadBalancer lbToCheck, LoadBalancer lbBeingShared) {
if ((lbBeingShared.getProtocol() == DNS_TCP && lbToCheck.getProtocol() == DNS_UDP)
|| (lbBeingShared.getProtocol() == DNS_UDP && lbToCheck.getProtocol() == DNS_TCP)) {
return true;
} else if (lbBeingShared.getProtocol() == UDP_STREAM
&& (lbToCheck.getProtocol() == LoadBalancerProtocol.TCP || lbToCheck.getProtocol() == LoadBalancerProtocol.TCP_CLIENT_FIRST)) {
return true;
} else if (lbBeingShared.getProtocol() == LoadBalancerProtocol.UDP
&& (lbToCheck.getProtocol() == LoadBalancerProtocol.TCP || lbToCheck.getProtocol() == LoadBalancerProtocol.TCP_CLIENT_FIRST)) {
return true;
} else if (lbToCheck.getProtocol() == UDP_STREAM
&& (lbBeingShared.getProtocol() == LoadBalancerProtocol.TCP || lbBeingShared.getProtocol() == LoadBalancerProtocol.TCP_CLIENT_FIRST)) {
return true;
} else if (lbToCheck.getProtocol() == LoadBalancerProtocol.UDP
&& (lbBeingShared.getProtocol() == LoadBalancerProtocol.TCP || lbBeingShared.getProtocol() == LoadBalancerProtocol.TCP_CLIENT_FIRST)) {
return true;
}
return false;
}
@Transactional
private void joinIpv6OnLoadBalancer(LoadBalancer lb) {
Set<LoadBalancerJoinVip6> loadBalancerJoinVip6SetConfig = lb.getLoadBalancerJoinVip6Set();
lb.setLoadBalancerJoinVip6Set(null);
Set<LoadBalancerJoinVip6> newLbVip6Setconfig = new HashSet<LoadBalancerJoinVip6>();
lb.setLoadBalancerJoinVip6Set(newLbVip6Setconfig);
for (LoadBalancerJoinVip6 jv6 : loadBalancerJoinVip6SetConfig) {
LoadBalancerJoinVip6 jv = new LoadBalancerJoinVip6(lb.getPort(), lb, jv6.getVirtualIp());
virtualIpRepository.persist(jv);
}
}
@Override
public SessionPersistence getSessionPersistenceByAccountIdLoadBalancerId(Integer accountId, Integer loadbalancerId) throws EntityNotFoundException, DeletedStatusException, BadRequestException {
return loadBalancerRepository.getSessionPersistenceByAccountIdLoadBalancerId(accountId, loadbalancerId);
}
@Override
@Transactional
public List<LoadBalancer> reassignLoadBalancerHost(List<LoadBalancer> lbs) throws Exception {
List<LoadBalancer> invalidLbs = new ArrayList<LoadBalancer>();
List<LoadBalancer> validLbs = new ArrayList<LoadBalancer>();
LoadBalancer dbLb;
List<LoadBalancer> lbsNeededForSharedVips = verifySharedVipsOnLoadBalancers(lbs);
if (lbsNeededForSharedVips.size() > 0) {
String[] sharedVipLBArray = buildLbArray(lbsNeededForSharedVips);
throw new BadRequestException("Found LoadBalancer sharing virtual ips. LoadBalancers: " + StringUtilities.buildDelemtedListFromStringArray(sharedVipLBArray, ",") + " are missing, please include the missing load balancers and retry the request.");
}
for (LoadBalancer lb : lbs) {
dbLb = loadBalancerRepository.getById(lb.getId());
if (dbLb.isSticky()) {
invalidLbs.add(dbLb);
} else {
processSpecifiedOrDefaultHost(lb);
validLbs.add(lb);
}
}
if (!invalidLbs.isEmpty()) {
String[] invalidLbArray = buildLbArray(invalidLbs);
throw new BadRequestException("Found sticky LoadBalancers: " + StringUtilities.buildDelemtedListFromStringArray(invalidLbArray, ",") + " please remove and retry the request");
}
for (LoadBalancer lb : validLbs) {
// Everything's ok! Begin update...
setStatus(lb, LoadBalancerStatus.PENDING_UPDATE);
}
return validLbs;
}
private void processSpecifiedOrDefaultHost(LoadBalancer lb) throws EntityNotFoundException, BadRequestException, ClusterStatusException, NoAvailableClusterException {
Integer hostId = null;
Host specifiedHost;
if (lb.getHost() != null) {
hostId = lb.getHost().getId();
}
if (!lb.isSticky()) {
if (hostId != null) {
specifiedHost = hostService.getById(hostId);
if (!(specifiedHost.getHostStatus().equals(HostStatus.ACTIVE) || specifiedHost.getHostStatus().equals(HostStatus.ACTIVE_TARGET))) {
setStatus(lb, LoadBalancerStatus.ACTIVE);
throw new BadRequestException("Load balancers cannot move to a host(" + specifiedHost.getId() + ") that is not in ACTIVE or ACTIVE_TARGET status.");
}
lb.setHost(specifiedHost);
} else {
lb.setHost(hostService.getDefaultActiveHostAndActiveCluster(lb.getAccountId()));
}
}
}
@Transactional
@Override
public boolean setErrorPage(Integer lid, Integer accountId, String content) throws EntityNotFoundException, ImmutableEntityException, UnprocessableEntityException {
if (!testAndSetStatus(accountId, lid, LoadBalancerStatus.PENDING_UPDATE)) {
String message = "Load balancer is considered immutable and cannot process request";
LOG.warn(message);
throw new ImmutableEntityException(message);
}
return loadBalancerRepository.setErrorPage(lid, accountId, content);
}
@Transactional
@Override
public boolean setDefaultErrorPage(String content) throws EntityNotFoundException {
return loadBalancerRepository.setDefaultErrorPage(content);
}
@Transactional
@Override
public boolean removeErrorPage(Integer lid, Integer accountId) throws EntityNotFoundException, UnprocessableEntityException, ImmutableEntityException {
if (!testAndSetStatus(accountId, lid, LoadBalancerStatus.PENDING_UPDATE)) {
String message = "Load balancer is considered immutable and cannot process request";
LOG.warn(message);
throw new ImmutableEntityException(message);
}
return loadBalancerRepository.removeErrorPage(lid, accountId);
}
@Transactional
@Override
public List<LoadBalancer> getLoadBalancersWithNode(String nodeAddress, Integer accountId) {
List<LoadBalancer> retLbs = loadBalancerRepository.getAllWithNode(nodeAddress, accountId);
List<LoadBalancer> domainLbs = new ArrayList<LoadBalancer>();
for (LoadBalancer loadbalancer : retLbs) {
LoadBalancer lb = new LoadBalancer();
lb.setName(loadbalancer.getName());
lb.setId(loadbalancer.getId());
lb.setStatus(loadbalancer.getStatus());
domainLbs.add(loadbalancer);
}
return domainLbs;
}
@Override
public List<LoadBalancer> getLoadBalancersWithUsage(Integer accountId, Calendar startTime, Calendar endTime, Integer offset, Integer limit) {
List<LoadBalancer> domainLbs;
domainLbs = loadBalancerRepository.getLoadBalancersActiveInRange(accountId, startTime, endTime, offset, limit);
return domainLbs;
}
@Override
public boolean isServiceNetLoadBalancer(Integer lbId) {
try {
final Set<VirtualIp> vipsByAccountIdLoadBalancerId = loadBalancerRepository.getVipsByLbId(lbId);
for (VirtualIp virtualIp : vipsByAccountIdLoadBalancerId) {
if (virtualIp.getVipType().equals(VirtualIpType.SERVICENET)) return true;
}
} catch (EntityNotFoundException e) {
return false;
} catch (DeletedStatusException e) {
return false;
}
return false;
}
@Override
public BitTags getCurrentBitTags(Integer lbId) {
BitTags bitTags = new BitTags();
try {
SslTermination sslTerm = sslTerminationRepository.getSslTerminationByLbId(lbId);
if (sslTerm.isEnabled()) {
bitTags.flipTagOn(BitTag.SSL);
if (!sslTerm.isSecureTrafficOnly()) {
bitTags.flipTagOn(BitTag.SSL_MIXED_MODE);
}
}
} catch (EntityNotFoundException e1) {
bitTags.flipTagOff(BitTag.SSL);
bitTags.flipTagOff(BitTag.SSL_MIXED_MODE);
}
if (isServiceNetLoadBalancer(lbId)) {
bitTags.flipTagOn(BitTag.SERVICENET_LB);
}
return bitTags;
}
private List<LoadBalancer> verifySharedVipsOnLoadBalancers(List<LoadBalancer> lbs) throws EntityNotFoundException, BadRequestException {
List<LoadBalancer> lbsWithSharedVips = new ArrayList<LoadBalancer>();
List<LoadBalancer> lbsNeededForRequest = new ArrayList<LoadBalancer>();
for (LoadBalancer lb : lbs) {
LoadBalancer dbLb = loadBalancerRepository.getById(lb.getId());
Set<LoadBalancerJoinVip> vip4Set = dbLb.getLoadBalancerJoinVipSet();
for (LoadBalancerJoinVip lbjv : vip4Set) {
List<LoadBalancer> lbsSharingVip4 = virtualIpRepository.getLoadBalancersByVipId(lbjv.getVirtualIp().getId());
lbsWithSharedVips.addAll(lbsSharingVip4);
}
Set<LoadBalancerJoinVip6> vip6Set = dbLb.getLoadBalancerJoinVip6Set();
for (LoadBalancerJoinVip6 lbjv : vip6Set) {
List<LoadBalancer> lbsSharingVip6 = virtualIpRepository.getLoadBalancersByVipId(lbjv.getVirtualIp().getId());
lbsWithSharedVips.addAll(lbsSharingVip6);
}
}
if (lbsWithSharedVips.size() > 0) {
for (LoadBalancer lbsv : lbsWithSharedVips) {
if (!buildLbIdList(lbs).contains(lbsv.getId())) {
lbsNeededForRequest.add(lbsv);
}
}
}
return lbsNeededForRequest;
}
private String[] buildLbArray(List<LoadBalancer> loadBalancers) {
String[] loadbalancersArray = new String[loadBalancers.size()];
for (int i = 0; i < loadBalancers.size(); i++) {
loadbalancersArray[i] = loadBalancers.get(i).getId().toString();
}
return loadbalancersArray;
}
private List<Integer> buildLbIdList(List<LoadBalancer> loadBalancers) {
List<Integer> incommingLbIds = new ArrayList<Integer>();
for (LoadBalancer lb : loadBalancers) {
incommingLbIds.add(lb.getId());
}
return incommingLbIds;
}
}