package org.openstack.atlas.api.integration;
import org.apache.axis.AxisFault;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openstack.atlas.adapter.LoadBalancerEndpointConfiguration;
import org.openstack.atlas.adapter.exceptions.InsufficientRequestException;
import org.openstack.atlas.adapter.exceptions.RollBackException;
import org.openstack.atlas.adapter.exceptions.StmRollBackException;
import org.openstack.atlas.adapter.helpers.IpHelper;
import org.openstack.atlas.adapter.service.ReverseProxyLoadBalancerStmAdapter;
import org.openstack.atlas.api.helpers.CacheKeyGen;
import org.openstack.atlas.api.helpers.DateHelpers;
import org.openstack.atlas.cfg.Configuration;
import org.openstack.atlas.cfg.PublicApiServiceConfigurationKeys;
import org.openstack.atlas.service.domain.cache.AtlasCache;
import org.openstack.atlas.service.domain.entities.*;
import org.openstack.atlas.service.domain.exceptions.EntityNotFoundException;
import org.openstack.atlas.service.domain.pojos.Stats;
import org.openstack.atlas.service.domain.pojos.ZeusSslTermination;
import org.openstack.atlas.service.domain.services.HealthMonitorService;
import org.openstack.atlas.service.domain.services.HostService;
import org.openstack.atlas.service.domain.services.LoadBalancerService;
import org.openstack.atlas.service.domain.services.NotificationService;
import org.openstack.atlas.util.crypto.CryptoUtil;
import org.openstack.atlas.util.crypto.exception.DecryptException;
import org.openstack.atlas.util.debug.Debug;
import org.rackspace.stingray.client.counters.VirtualServerStats;
import org.rackspace.stingray.client.exception.StingrayRestClientException;
import org.rackspace.stingray.client.exception.StingrayRestClientObjectNotFoundException;
import java.net.MalformedURLException;
import java.net.URI;
import java.rmi.RemoteException;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
import static java.util.Calendar.getInstance;
public class ReverseProxyLoadBalancerServiceStmImpl implements ReverseProxyLoadBalancerStmService {
final Log LOG = LogFactory.getLog(ReverseProxyLoadBalancerServiceStmImpl.class);
private ReverseProxyLoadBalancerStmAdapter reverseProxyLoadBalancerStmAdapter;
private LoadBalancerService loadBalancerService;
private HostService hostService;
private NotificationService notificationService;
private HealthMonitorService healthMonitorService;
private Configuration configuration;
private AtlasCache atlasCache;
public void setLoadBalancerService(LoadBalancerService loadBalancerService) {
this.loadBalancerService = loadBalancerService;
}
public void setHostService(HostService hostService) {
this.hostService = hostService;
}
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void setHealthMonitorService(HealthMonitorService healthMonitorService) {
this.healthMonitorService = healthMonitorService;
}
@Override
public void createLoadBalancer(LoadBalancer lb) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.createLoadBalancer(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void updateLoadBalancer(LoadBalancer lb, LoadBalancer queLb, UserPages up) throws InsufficientRequestException, RollBackException, EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateLoadBalancer(config, lb, queLb, up);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void deleteLoadBalancer(LoadBalancer lb) throws RemoteException, InsufficientRequestException, RollBackException, EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteLoadBalancer(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void setRateLimit(LoadBalancer loadBalancer, RateLimit rateLimit) throws RemoteException, InsufficientRequestException, RollBackException, EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.setRateLimit(config, loadBalancer, rateLimit);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void deleteRateLimit(LoadBalancer loadBalancer) throws RemoteException, InsufficientRequestException, RollBackException, EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteRateLimit(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void updateRateLimit(LoadBalancer loadBalancer, RateLimit rateLimit) throws RemoteException, InsufficientRequestException, RollBackException, EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateRateLimit(config, loadBalancer, rateLimit);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void changeHostForLoadBalancer(LoadBalancer lb, Host newHost) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.changeHostForLoadBalancer(config, lb, newHost);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void addVirtualIps(Integer lbId, Integer accountId, LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lbId);
try {
reverseProxyLoadBalancerStmAdapter.updateVirtualIps(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void setNodes(LoadBalancer lb) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.setNodes(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void removeNode(LoadBalancer lb, Node node) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.removeNode(config, lb, node);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void removeNodes(LoadBalancer lb, List<Node> nodesToRemove) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.removeNodes(config, lb, nodesToRemove);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void updateAccessList(LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateAccessList(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void deleteAccessList(LoadBalancer lb, List<Integer> accessListToDelete) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteAccessList(config, lb, accessListToDelete);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void updateConnectionThrottle(LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateProtection(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void deleteConnectionThrottle(LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteConnectionThrottle(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void updateHealthMonitor(LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateHealthMonitor(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void removeHealthMonitor(LoadBalancer loadBalancer) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteHealthMonitor(config, loadBalancer);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void suspendLoadBalancer(LoadBalancer lb) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.addSuspension(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void removeSuspension(LoadBalancer lb) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.removeSuspension(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void deleteVirtualIps(LoadBalancer lb, List<Integer> ids, UserPages up) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteVirtualIps(config, lb, ids, up);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public boolean isEndPointWorking(Host host) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
boolean out;
LoadBalancerEndpointConfiguration hostConfig = getConfigHost(host);
out = reverseProxyLoadBalancerStmAdapter.isEndPointWorking(hostConfig);
return out;
}
//Deprecated
// @Override
// public Hostssubnet getSubnetMappings(Host host) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
// LoadBalancerEndpointConfiguration hostConfig = getConfig(host);
// String hostName = host.getTrafficManagerName();
// Hostssubnet hostssubnet;
// try {
// hostssubnet = reverseProxyLoadBalancerStmAdapter.getSubnetMappings(getConfig(host), hostName);
// } catch (RollBackException af) {
// checkAndSetIfSoapEndPointBad(hostConfig, af);
// throw af;
// }
// return hostssubnet;
// }
//
// @Override
// public void setSubnetMappings(Host host, Hostssubnet hostssubnet) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
// LoadBalancerEndpointConfiguration hostConfig = getConfig(host);
// String hostName = host.getTrafficManagerName();
// try {
// reverseProxyLoadBalancerStmAdapter.setSubnetMappings(hostConfig, hostssubnet);
// } catch (RollBackException af) {
// checkAndSetIfSoapEndPointBad(hostConfig, af);
// throw af;
// }
// }
//
// @Override
// public void deleteSubnetMappings(Host host, Hostssubnet hostssubnet) throws InsufficientRequestException, RollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
// LoadBalancerEndpointConfiguration hostConfig = getConfig(host);
// String hostName = host.getTrafficManagerName();
// try {
// reverseProxyLoadBalancerStmAdapter.deleteSubnetMappings(hostConfig, hostssubnet);
// } catch (RollBackException af) {
// checkAndSetIfSoapEndPointBad(hostConfig, af);
// throw af;
// }
// }
@Override
public void deleteErrorFile(LoadBalancer loadBalancer, UserPages up) throws MalformedURLException, EntityNotFoundException, DecryptException, InsufficientRequestException, RemoteException, StmRollBackException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.deleteErrorFile(config, loadBalancer, up);
} catch (StmRollBackException ex) {
checkAndSetIfRestEndPointBad(config, ex);
throw ex;
}
}
@Override
public void setErrorFile(LoadBalancer loadBalancer, String content)
throws InsufficientRequestException, StmRollBackException, MalformedURLException, EntityNotFoundException, DecryptException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.setErrorFile(config, loadBalancer, content);
} catch (StmRollBackException ex) {
checkAndSetIfRestEndPointBad(config, ex);
throw ex;
}
}
@Override
public void uploadDefaultErrorFile(Integer clusterId, String content) throws MalformedURLException, EntityNotFoundException, DecryptException, InsufficientRequestException, RemoteException, RollBackException {
LoadBalancerEndpointConfiguration config = getConfigbyClusterId(clusterId);
try {
reverseProxyLoadBalancerStmAdapter.uploadDefaultErrorFile(config, content);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
LoadBalancerEndpointConfiguration getConfigbyClusterId(Integer clusterId) throws EntityNotFoundException, DecryptException {
Cluster cluster = hostService.getClusterById(clusterId);
Host soapEndpointHost = hostService.getRestEndPointHost(cluster.getId());
List<String> failoverHostNames = hostService.getFailoverHostNames(cluster.getId());
String logFileLocation = configuration.getString(PublicApiServiceConfigurationKeys.access_log_file_location);
List<Host> failoverHosts = hostService.getFailoverHosts(cluster.getId());
return new LoadBalancerEndpointConfiguration(soapEndpointHost, cluster.getUsername(), CryptoUtil.decrypt(cluster.getPassword()), soapEndpointHost, failoverHostNames, logFileLocation, failoverHosts);
}
// Send request to proper SOAPEndpoint(Calculated by the database) for host's traffic manager
@Override
public LoadBalancerEndpointConfiguration getConfig(Host host) throws DecryptException, MalformedURLException {
Cluster cluster = host.getCluster();
Host soapEndpointHost = hostService.getRestEndPointHost(cluster.getId());
List<String> failoverHostNames = hostService.getFailoverHostNames(cluster.getId());
List<Host> failoverHosts = hostService.getFailoverHosts(cluster.getId());
String logFileLocation = configuration.getString(PublicApiServiceConfigurationKeys.access_log_file_location);
return new LoadBalancerEndpointConfiguration(soapEndpointHost, cluster.getUsername(), CryptoUtil.decrypt(cluster.getPassword()), host, failoverHostNames, logFileLocation, failoverHosts);
}
// Send SOAP request directly to the hosts traffic manager.
@Override
public LoadBalancerEndpointConfiguration getConfigHost(Host host) throws DecryptException, MalformedURLException {
Cluster cluster = host.getCluster();
List<String> failoverHostNames = hostService.getFailoverHostNames(cluster.getId());
List<Host> failoverHosts = hostService.getFailoverHosts(cluster.getId());
String logFileLocation = configuration.getString(PublicApiServiceConfigurationKeys.access_log_file_location);
return new LoadBalancerEndpointConfiguration(host, cluster.getUsername(), CryptoUtil.decrypt(cluster.getPassword()), host, failoverHostNames, logFileLocation, failoverHosts);
}
private LoadBalancerEndpointConfiguration getConfigbyLoadBalancerId(Integer lbId) throws EntityNotFoundException, DecryptException, MalformedURLException {
LoadBalancer loadBalancer = loadBalancerService.get(lbId);
Host host = loadBalancer.getHost();
Cluster cluster = host.getCluster();
Host soapEndpointHost = hostService.getRestEndPointHost(cluster.getId());
List<String> failoverHostNames = hostService.getFailoverHostNames(cluster.getId());
List<Host> failoverHosts = hostService.getFailoverHosts(cluster.getId());
String logFileLocation = configuration.getString(PublicApiServiceConfigurationKeys.access_log_file_location);
return new LoadBalancerEndpointConfiguration(soapEndpointHost, cluster.getUsername(), CryptoUtil.decrypt(cluster.getPassword()), host, failoverHostNames, logFileLocation, failoverHosts);
}
public void setReverseProxyLoadBalancerStmAdapter(ReverseProxyLoadBalancerStmAdapter reverseProxyLoadBalancerStmAdapter) {
this.reverseProxyLoadBalancerStmAdapter = reverseProxyLoadBalancerStmAdapter;
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
private void checkAndSetIfRestEndPointBad(LoadBalancerEndpointConfiguration config, StmRollBackException ex) {
Host configuredHost = config.getEndpointUrlHost();
if (IpHelper.isNetworkConnectionException(ex)) {
LOG.error(String.format("STM endpoint %s went bad marking host[%d] as bad. Exception was %s", configuredHost.getEndpoint(), configuredHost.getId(), Debug.getExtendedStackTrace(ex)));
configuredHost.setRestEndpointActive(Boolean.FALSE);
hostService.update(configuredHost);
} else {
LOG.warn(String.format("STM endpoint %s on host[%d] throw an STM Fault but not marking as bad as it was not a network connection error: Exception was %s", configuredHost.getEndpoint(), configuredHost.getId(), Debug.getExtendedStackTrace(ex)));
}
}
private void checkAndSetIfRestEndPointBad(LoadBalancerEndpointConfiguration config, RollBackException af) {
Host configuredHost = config.getEndpointUrlHost();
if (IpHelper.isNetworkConnectionException(af)) {
LOG.error(String.format("SOAP endpoint %s went bad marking host[%d] as bad. Exception was %s", configuredHost.getEndpoint(), configuredHost.getId(), Debug.getExtendedStackTrace(af)));
configuredHost.setRestEndpointActive(Boolean.FALSE);
hostService.update(configuredHost);
}
LOG.warn(String.format("SOAP endpoint %s on host[%d] throw an RollBackException but not marking as bad as it was not a network connection error: Exception was %s", configuredHost.getEndpoint(), configuredHost.getId(), Debug.getExtendedStackTrace(af)));
}
@Override
public void updateSslTermination(LoadBalancer loadBalancer, ZeusSslTermination sslTermination, UserPages up) throws MalformedURLException, EntityNotFoundException, DecryptException, InsufficientRequestException, RollBackException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(loadBalancer.getId());
try {
reverseProxyLoadBalancerStmAdapter.updateSslTermination(config, loadBalancer, sslTermination, up);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public void removeSslTermination(LoadBalancer lb) throws RemoteException, MalformedURLException, EntityNotFoundException, DecryptException, InsufficientRequestException, RollBackException {
LoadBalancerEndpointConfiguration config = getConfigbyLoadBalancerId(lb.getId());
try {
reverseProxyLoadBalancerStmAdapter.removeSslTermination(config, lb);
} catch (RollBackException af) {
checkAndSetIfRestEndPointBad(config, af);
throw af;
}
}
@Override
public Stats getVirtualServerStats(LoadBalancer loadBalancer, URI endpoint) throws EntityNotFoundException, MalformedURLException, DecryptException, InsufficientRequestException, StingrayRestClientObjectNotFoundException, StingrayRestClientException {
Integer accountId = loadBalancer.getAccountId();
Integer loadbalancerId = loadBalancer.getId();
LoadBalancerEndpointConfiguration config = getConfigHost(loadBalancerService.get(loadbalancerId).getHost());
String key = CacheKeyGen.generateKeyName(accountId, loadbalancerId);
Stats stats;
long cal = getInstance().getTimeInMillis();
stats = (Stats) atlasCache.get(key);
if (stats == null) {
stats = reverseProxyLoadBalancerStmAdapter.getVirtualServerStats(config, loadBalancer);
LOG.info("Date:" + DateHelpers.getDate(Calendar.getInstance().getTime()) + " AccountID: " + accountId + " GetLoadBalancerStats, Missed from cache, retrieved from api... Time taken: " + DateHelpers.getTotalTimeTaken(cal) + " ms");
atlasCache.set(key, stats);
} else {
LOG.info("Date:" + DateHelpers.getDate(Calendar.getInstance().getTime()) + " AccountID: " + accountId + " GetLoadBalancerStats, retrieved from cache... Time taken: " + DateHelpers.getTotalTimeTaken(cal) + " ms");
return stats;
}
return stats;
}
public void setAtlasCache(AtlasCache atlasCache) {
this.atlasCache = atlasCache;
}
public AtlasCache getAtlasCache() {
return atlasCache;
}
}