package org.openstack.atlas.service.domain.services.impl;
import com.sun.jersey.api.client.ClientResponse;
import java.util.logging.Level;
import java.util.logging.Logger;
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.services.AccountLimitService;
import org.openstack.atlas.service.domain.services.LoadBalancerStatusHistoryService;
import org.openstack.atlas.service.domain.services.VirtualIpService;
import org.openstack.atlas.service.domain.services.helpers.StringHelper;
import org.openstack.atlas.service.domain.util.Constants;
import org.openstack.atlas.service.domain.util.StringUtilities;
import org.openstack.atlas.util.common.ListUtil;
import org.openstack.atlas.util.crypto.HashUtil;
import org.openstack.atlas.util.ip.IPv6;
import org.openstack.atlas.util.ip.IPv6Cidr;
import org.openstack.atlas.util.ip.exception.IPStringConversionException;
import org.openstack.atlas.util.ip.exception.IpTypeMissMatchException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import org.openstack.atlas.restclients.dns.StaticDNSClientUtils;
import org.openstack.atlas.service.domain.events.entities.CategoryType;
import org.openstack.atlas.service.domain.events.entities.EventSeverity;
import org.openstack.atlas.service.domain.events.entities.EventType;
import org.openstack.atlas.service.domain.events.entities.LoadBalancerServiceEvent;
import org.openstack.atlas.service.domain.services.helpers.RdnsHelper;
import org.openstack.atlas.util.debug.Debug;
@Service
public class VirtualIpServiceImpl extends BaseService implements VirtualIpService {
private final Log LOG = LogFactory.getLog(VirtualIpServiceImpl.class);
private static final int SB_INIT_SIZE = 1024 * 8;
public static final String DEL_PTR_FAILED = "Delete PTR on Virtual IP Fail";
public static final String DEL_PTR_PASSED = "Delete PTR on Virtual IP Passed";
@Autowired
private AccountLimitService accountLimitService;
@Autowired
private LoadBalancerStatusHistoryService loadBalancerStatusHistoryService;
@Override
public VirtualIp get(Integer id) throws EntityNotFoundException {
return virtualIpRepository.getById(id);
}
@Override
public Set<VirtualIp> get(Integer accountId, Integer loadBalancerId, Integer offset, Integer limit, Integer marker) throws EntityNotFoundException, DeletedStatusException {
return loadBalancerRepository.getVipsByLbId(loadBalancerId, offset, limit, marker);
}
@Override
public Set<VirtualIpv6> getVirtualIpv6ByAccountIdLoadBalancerId(Integer aid, Integer lid) throws EntityNotFoundException {
LoadBalancer lb = loadBalancerRepository.getByIdAndAccountId(lid, aid);
return getVirtualIpv6ByLoadBalancerId(lid);
}
@Override
public Set<VirtualIpv6> getVirtualIpv6ByLoadBalancerId(Integer lid) throws EntityNotFoundException {
return virtualIpv6Repository.getVirtualIpv6sByLoadBalancerId(lid);
}
@Override
public List<org.openstack.atlas.service.domain.entities.LoadBalancer> getLoadBalancerByVipId(Integer vipId) {
return virtualIpRepository.getLoadBalancersByVipId(vipId);
}
@Override
public List<org.openstack.atlas.service.domain.entities.LoadBalancer> getLoadBalancerByVipAddress(String address) {
return virtualIpRepository.getLoadBalancerByVipAddress(address);
}
@Override
public List<LoadBalancer> getLoadBalancerByVip6Address(String address) throws IPStringConversionException, EntityNotFoundException {
List<Integer> accountIds;
List<LoadBalancer> out = new ArrayList<LoadBalancer>();
Integer clusterId = null;
Integer accountId = null;
Integer vipOctets = null;
VirtualIpv6 vip6;
IPv6 ipv6;
boolean matchfound;
List<LoadBalancer> loadbalancers;
ipv6 = new IPv6();
loadbalancers = new ArrayList<LoadBalancer>();
ipv6 = new IPv6(address);
ipv6.expand();
try { // Find the vip octet
vipOctets = ipv6.getVipOctets();
} catch (IPStringConversionException ex) {
vipOctets = null;
}
String[] strSplit = address.split(":");
String searchSha = String.format("%s%s", strSplit[4], strSplit[5]);
IPv6Cidr searchCidr = new IPv6Cidr(String.format("%s:%s:%s:%s::/64",
strSplit[0], strSplit[1], strSplit[2], strSplit[3]));
for (Cluster cl : clusterRepository.getAll()) {
try {
IPv6Cidr foundCidr = new IPv6Cidr(cl.getClusterIpv6Cidr());
try {
matchfound = foundCidr.matches(searchCidr);
if (matchfound) { // This must be our cluster since the cidrs matched.
clusterId = cl.getId();
break;
}
} catch (IpTypeMissMatchException ex) {
continue; // This one can't be it. Coulden't even match the IP type :(
}
} catch (IPStringConversionException ex) {
continue; // This one is an even bigger fail pfft.
}
} // Hopefully we recovered the cluster from the Vip otherwise its null;
accountIds = virtualIpRepository.getAccountBySha1Sum(searchSha);
if (accountIds.size() == 1) {
accountId = accountIds.get(0);
}
vip6 = virtualIpRepository.getVirtualIpv6BytClusterAccountOctet(clusterId, accountId, vipOctets);
return getLoadBalancerByVip6Id(vip6.getId());
}
@Override
public List<LoadBalancer> getLoadBalancerByVip6Id(Integer vip6Id) {
return virtualIpRepository.getLoadBalancerByVip6Address(vip6Id);
}
@Override
@Transactional
public VirtualIp allocateIpv4VirtualIp(VirtualIp vipConfig, Cluster cluster) throws OutOfVipsException {
Calendar timeConstraintForVipReuse = Calendar.getInstance();
timeConstraintForVipReuse.add(Calendar.DATE, -Constants.NUM_DAYS_BEFORE_VIP_REUSE);
if (vipConfig.getVipType() == null) {
vipConfig.setVipType(VirtualIpType.PUBLIC);
}
try {
return virtualIpRepository.allocateIpv4VipBeforeDate(cluster, timeConstraintForVipReuse, vipConfig.getVipType());
} catch (OutOfVipsException e) {
LOG.warn(String.format("Out of IPv4 virtual ips that were de-allocated before '%s'.", timeConstraintForVipReuse.getTime()));
try {
return virtualIpRepository.allocateIpv4VipAfterDate(cluster, timeConstraintForVipReuse, vipConfig.getVipType());
} catch (OutOfVipsException e2) {
e2.printStackTrace();
throw e2;
}
}
}
@Override
@Transactional(rollbackFor = {EntityNotFoundException.class, UnprocessableEntityException.class, ImmutableEntityException.class, BadRequestException.class, OutOfVipsException.class, UniqueLbPortViolationException.class, AccountMismatchException.class})
public VirtualIp addVirtualIpToLoadBalancer(VirtualIp vipConfig, LoadBalancer lb, Ticket ticket) throws EntityNotFoundException, UnprocessableEntityException, ImmutableEntityException, BadRequestException, OutOfVipsException, UniqueLbPortViolationException, AccountMismatchException {
VirtualIp vipToAdd;
LoadBalancer dbLoadBalancer = loadBalancerRepository.getById(lb.getId());
if (vipConfig.getId() != null) {
vipToAdd = virtualIpRepository.getById(vipConfig.getId());
if (!vipTypeMatchesTypeForLoadBalancer(vipToAdd.getVipType(), dbLoadBalancer)) {
String message = StringHelper.mismatchingVipType(vipConfig.getVipType());
LOG.debug(message);
throw new BadRequestException(message);
}
if (!doesVipBelongToAccount(vipToAdd, dbLoadBalancer.getAccountId())) {
String message = "The requested virtual ip does not belong to this account";
LOG.debug(message);
throw new AccountMismatchException(message);
}
if (!isVipConfiguredOnHost(vipToAdd, dbLoadBalancer.getHost())) {
String message = "The virtual ip being added has a different host than the host for this loadbalancer";
LOG.debug(message);
throw new BadRequestException(message);
}
if (isVipPortCombinationInUse(vipToAdd, dbLoadBalancer.getPort())) {
String message = "The virtual ip and load balancer port combination is currently in use";
LOG.debug(message);
throw new UniqueLbPortViolationException(message);
}
} else {
if (!vipTypeMatchesTypeForLoadBalancer(vipConfig.getVipType(), dbLoadBalancer)) {
String message = StringHelper.mismatchingVipType(vipConfig.getVipType());
LOG.debug(message);
throw new BadRequestException(message);
}
vipToAdd = allocateIpv4VirtualIp(vipConfig, dbLoadBalancer.getHost().getCluster());
}
if (!loadBalancerRepository.testAndSetStatus(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_UPDATE, false)) {
String message = StringHelper.immutableLoadBalancer(dbLoadBalancer);
LOG.warn(message);
throw new ImmutableEntityException(message);
}
loadBalancerRepository.createTicket(dbLoadBalancer, ticket);
dbLoadBalancer.getLoadBalancerJoinVipSet().add(new LoadBalancerJoinVip(dbLoadBalancer.getPort(), dbLoadBalancer, vipToAdd));
loadBalancerRepository.update(dbLoadBalancer);
return vipToAdd;
}
@Override
@Transactional(rollbackFor = {EntityNotFoundException.class, UnprocessableEntityException.class, ImmutableEntityException.class, BadRequestException.class, OutOfVipsException.class, UniqueLbPortViolationException.class, AccountMismatchException.class, LimitReachedException.class})
public VirtualIpv6 addIpv6VirtualIpToLoadBalancer(VirtualIpv6 vipConfig, LoadBalancer lb) throws EntityNotFoundException, UnprocessableEntityException, ImmutableEntityException, BadRequestException, OutOfVipsException, UniqueLbPortViolationException, AccountMismatchException, LimitReachedException {
VirtualIpv6 vipToAdd;
LoadBalancer dlb = loadBalancerRepository.getByIdAndAccountId(lb.getId(), lb.getAccountId());
Integer ipv6Limit = accountLimitService.getLimit(dlb.getAccountId(), AccountLimitType.IPV6_LIMIT);
if (dlb.getLoadBalancerJoinVip6Set().size() >= ipv6Limit) {
throw new LimitReachedException(String.format("Your load balancer cannot have more than %d IPv6 virtual ips.", ipv6Limit));
}
if (vipConfig.getId() != null) {
try {
vipToAdd = virtualIpv6Repository.getById(vipConfig.getId());
} catch (EntityNotFoundException enfe) {
LOG.debug(String.format("The id specified: %s could not be found. This is a request to a IPV4 virtual ip id. Or it is indeed a non-existent virutal ip. Account: %s. LoadBalancer: %s. %s", vipConfig.getId(), lb.getAccountId(), lb.getId(), enfe.getMessage()));
throw new EntityNotFoundException("Please provide a valid IPV6 virtual ip.");
}
if (!vipTypeMatchesTypeForLoadBalancer(VirtualIpType.PUBLIC, dlb)) {
String message = StringHelper.mismatchingVipType(VirtualIpType.PUBLIC);
LOG.debug(message);
throw new BadRequestException(message);
}
if (!isIpv6VipConfiguredOnHost(vipToAdd, dlb.getHost())) {
String message = "The virtual ip being added has a different host than the host for this loadbalancer";
LOG.debug(message);
throw new BadRequestException(message);
}
if (isIpv6VipPortCombinationInUse(vipToAdd, dlb.getPort())) {
String message = "The virtual ip and load balancer port combination is currently in use";
LOG.debug(message);
throw new UniqueLbPortViolationException(message);
}
} else {
if (!vipTypeMatchesTypeForLoadBalancer(VirtualIpType.PUBLIC, dlb)) {
String message = StringHelper.mismatchingVipType(VirtualIpType.PUBLIC);
LOG.debug(message);
throw new BadRequestException(message);
}
vipToAdd = allocateIpv6VirtualIp(dlb);
}
if (!loadBalancerRepository.testAndSetStatus(dlb.getAccountId(), dlb.getId(), LoadBalancerStatus.PENDING_UPDATE, false)) {
String message = StringHelper.immutableLoadBalancer(dlb);
LOG.warn(message);
throw new ImmutableEntityException(message);
} else {
//Set status record
loadBalancerStatusHistoryService.save(dlb.getAccountId(), dlb.getId(), LoadBalancerStatus.PENDING_UPDATE);
}
LoadBalancerJoinVip6 jv = new LoadBalancerJoinVip6(dlb.getPort(), dlb, vipToAdd);
virtualIpRepository.persist(jv);
return vipToAdd;
}
@Override
@Transactional
public boolean isVipAllocatedToMultipleLoadBalancers(VirtualIp virtualIp) {
final int MIN_NUM_LBS_FOR_VIP = 1;
Long numLoadBalancersAttachedToVip = virtualIpRepository.getNumLoadBalancersAttachedToVip(virtualIp);
LOG.debug(String.format("%d load balancer(s) currently using vip '%d'", numLoadBalancersAttachedToVip, virtualIp.getId()));
return numLoadBalancersAttachedToVip > MIN_NUM_LBS_FOR_VIP;
}
@Override
@Transactional
public boolean isVipAllocatedToAnyLoadBalancer(VirtualIp virtualIp) {
Long numLoadBalancersAttachedToVip = virtualIpRepository.getNumLoadBalancersAttachedToVip(virtualIp);
LOG.debug(String.format("%d load balancer(s) currently using vip '%d'", numLoadBalancersAttachedToVip, virtualIp.getId()));
return numLoadBalancersAttachedToVip > 0;
}
@Override
@Transactional
public boolean isVipAllocatedToAnotherLoadBalancer(LoadBalancer lb, VirtualIp virtualIp) {
List<LoadBalancerJoinVip> joinRecords = virtualIpRepository.getJoinRecordsForVip(virtualIp);
for (LoadBalancerJoinVip joinRecord : joinRecords) {
if (!joinRecord.getLoadBalancer().getId().equals(lb.getId())) {
LOG.debug(String.format("Virtual ip '%d' is used by a load balancer other than load balancer '%d'.", virtualIp.getId(), lb.getId()));
return true;
}
}
return false;
}
@Override
@Transactional
public boolean isIpv6VipAllocatedToAnotherLoadBalancer(LoadBalancer lb, VirtualIpv6 virtualIp) {
List<LoadBalancerJoinVip6> joinRecords = virtualIpv6Repository.getJoinRecordsForVip(virtualIp);
for (LoadBalancerJoinVip6 joinRecord : joinRecords) {
if (!joinRecord.getLoadBalancer().getId().equals(lb.getId())) {
LOG.debug(String.format("IPv6 virtual ip '%d' is used by a load balancer other than load balancer '%d'.", virtualIp.getId(), lb.getId()));
return true;
}
}
return false;
}
@Override
@Transactional
public void removeAllVipsFromLoadBalancer(LoadBalancer lb) {
for (LoadBalancerJoinVip loadBalancerJoinVip : lb.getLoadBalancerJoinVipSet()) {
virtualIpRepository.removeJoinRecord(loadBalancerJoinVip);
reclaimVirtualIp(lb, loadBalancerJoinVip.getVirtualIp());
}
lb.getLoadBalancerJoinVipSet().clear();
for (LoadBalancerJoinVip6 loadBalancerJoinVip6 : lb.getLoadBalancerJoinVip6Set()) {
virtualIpv6Repository.removeJoinRecord(loadBalancerJoinVip6);
reclaimIpv6VirtualIp(lb, loadBalancerJoinVip6.getVirtualIp());
}
lb.getLoadBalancerJoinVip6Set().clear();
}
@Override
@Transactional
public void removeVipFromLoadBalancer(LoadBalancer lb, Integer vipId) {
for (LoadBalancerJoinVip loadBalancerJoinVip : lb.getLoadBalancerJoinVipSet()) {
if (loadBalancerJoinVip.getVirtualIp().getId().equals(vipId)) {
virtualIpRepository.removeJoinRecord(loadBalancerJoinVip);
reclaimVirtualIp(lb, loadBalancerJoinVip.getVirtualIp());
lb.getLoadBalancerJoinVipSet().remove(loadBalancerJoinVip);
break;
}
}
for (LoadBalancerJoinVip6 loadBalancerJoinVip6 : lb.getLoadBalancerJoinVip6Set()) {
if (loadBalancerJoinVip6.getVirtualIp().getId().equals(vipId)) {
virtualIpv6Repository.removeJoinRecord(loadBalancerJoinVip6);
reclaimIpv6VirtualIp(lb, loadBalancerJoinVip6.getVirtualIp());
lb.getLoadBalancerJoinVip6Set().remove(loadBalancerJoinVip6);
break;
}
}
}
@Override
@Transactional
public void removeVipsFromLoadBalancer(LoadBalancer lb, List<Integer> vipIds) {
for (Integer vipIdToDelete : vipIds) {
removeVipFromLoadBalancer(lb, vipIdToDelete);
}
}
@Override
@Transactional(rollbackFor = {EntityNotFoundException.class, ImmutableEntityException.class, UnprocessableEntityException.class, BadRequestException.class})
public void prepareForVirtualIpDeletion(LoadBalancer lb, Integer vipId) throws EntityNotFoundException, ImmutableEntityException, UnprocessableEntityException, BadRequestException {
List<Integer> vipIdsToDelete = new ArrayList<Integer>();
vipIdsToDelete.add(vipId);
prepareForVirtualIpsDeletion(lb.getAccountId(), lb.getId(), vipIdsToDelete);
}
@Override
@Transactional(rollbackFor = {EntityNotFoundException.class, ImmutableEntityException.class, UnprocessableEntityException.class, BadRequestException.class})
public void prepareForVirtualIpsDeletion(Integer accountId, Integer loadbalancerId, List<Integer> virtualIpIds) throws EntityNotFoundException, BadRequestException, UnprocessableEntityException, ImmutableEntityException {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(loadbalancerId, accountId);
if (!hasAtLeastMinRequiredVips(dbLoadBalancer, virtualIpIds)) {
LOG.debug("Updating the lb status to active");
throw new BadRequestException(String.format("Cannot delete virtual ips. Minimum number of virtual ips required is %d.", Constants.MIN_REQUIRED_VIPS));
}
List<Integer> badVipIds = doesVipsBelongToLoadBalancer(dbLoadBalancer, virtualIpIds);
if (!badVipIds.isEmpty()) {
LOG.debug("Updating the lb status to active");
throw new BadRequestException(String.format("Must provide valid virtual ips, %s could not be found.", StringUtilities.DelimitString(badVipIds, ",")));
}
if (!loadBalancerRepository.testAndSetStatus(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_UPDATE, false)) {
String message = StringHelper.immutableLoadBalancer(dbLoadBalancer);
LOG.warn(message);
throw new ImmutableEntityException(message);
} else {
//Set status record
loadBalancerStatusHistoryService.save(dbLoadBalancer.getAccountId(), dbLoadBalancer.getId(), LoadBalancerStatus.PENDING_UPDATE);
}
}
@Override
@Transactional
public boolean hasAtLeastMinRequiredVips(LoadBalancer lb, List<Integer> virtualIpIds) {
Long numVipsAssignedToLoadBalancer = virtualIpRepository.getNumIpv4VipsForLoadBalancer(lb);
numVipsAssignedToLoadBalancer += virtualIpv6Repository.getNumIpv6VipsForLoadBalancer(lb);
LOG.debug(String.format("%d vip(s) currently assigned to load balancer '%d'", numVipsAssignedToLoadBalancer, lb.getId()));
return (numVipsAssignedToLoadBalancer - virtualIpIds.size()) >= Constants.MIN_REQUIRED_VIPS;
}
@Override
@Transactional
public boolean hasExactlyMinRequiredVips(LoadBalancer lb) {
Long numVipsAssignedToLoadBalancer = virtualIpRepository.getNumIpv4VipsForLoadBalancer(lb);
numVipsAssignedToLoadBalancer += virtualIpv6Repository.getNumIpv6VipsForLoadBalancer(lb);
LOG.debug(String.format("%d vip(s) currently assigned to load balancer '%d'", numVipsAssignedToLoadBalancer, lb.getId()));
return numVipsAssignedToLoadBalancer == Constants.MIN_REQUIRED_VIPS;
}
@Override
@Transactional
public boolean doesVipBelongToLoadBalancer(LoadBalancer lb, Integer vipId) {
List<VirtualIp> vipsForLb = virtualIpRepository.getVipsByLoadBalancerId(lb.getId());
for (VirtualIp vipForLb : vipsForLb) {
if (vipId.equals(vipForLb.getId())) {
return true;
}
}
List<VirtualIpv6> ipv6VipsForLb = virtualIpv6Repository.getVipsByLoadBalancerId(lb.getId());
for (VirtualIpv6 vipForLb : ipv6VipsForLb) {
if (vipId.equals(vipForLb.getId())) {
return true;
}
}
return false;
}
@Override
@Transactional
public boolean doesVipBelongToAccount(VirtualIp virtualIp, Integer accountId) {
List<Integer> accountsUsingVip = virtualIpRepository.getAccountIds(virtualIp);
if (accountsUsingVip.size() > Constants.MIN_ACCOUNTS_PER_VIP) {
LOG.warn(String.format("Multiple accounts using virtual ip '%d'", virtualIp.getId()));
}
for (Integer accountUsingVip : accountsUsingVip) {
if (accountId.equals(accountUsingVip)) {
return true;
}
}
return false;
}
@Override
@Transactional
public void removeVipFromCluster(VirtualIp virtualIp) throws ImmutableEntityException, EntityNotFoundException {
virtualIp = virtualIpRepository.getById(virtualIp.getId());
LOG.info(String.format("Removing virtual ip '%d' from cluster...", virtualIp.getId()));
LOG.debug(String.format("Verifying that virtual ip '%d' is not in use by any load balancer.", virtualIp.getId()));
if (isVipAllocatedToAnyLoadBalancer(virtualIp)) {
LOG.warn(String.format("Virtual ip '%d' is in use by another load balancer and cannot be removed.", virtualIp.getId()));
throw new ImmutableEntityException("The virtual ip belongs to one or more load balancers.");
}
virtualIpRepository.deleteVirtualIp(virtualIp);
LOG.info(String.format("Successfully removed virtual ip '%d' from cluster.", virtualIp.getId()));
}
@Override
public List<VirtualIp> getVipsByClusterId(Integer clusterId) {
return virtualIpRepository.getVipsByClusterId(clusterId);
}
@Override
@Transactional
public void persist(Object obj) {
virtualIpRepository.persist(obj);
}
private void reclaimVirtualIp(LoadBalancer lb, VirtualIp virtualIp) {
if (!isVipAllocatedToAnotherLoadBalancer(lb, virtualIp)) {
delPtrRecord(lb.getAccountId(), lb.getId(), virtualIp.getIpAddress());
virtualIpRepository.deallocateVirtualIp(virtualIp);
Debug.nop();
}
}
private void reclaimIpv6VirtualIp(LoadBalancer lb, VirtualIpv6 virtualIpv6) {
String ip;
int aid = lb.getAccountId();
int lid = lb.getId();
RdnsHelper rdns = RdnsHelper.newRdnsHelper(aid);
LoadBalancerServiceEvent lbe;
if (!isIpv6VipAllocatedToAnotherLoadBalancer(lb, virtualIpv6)) {
try {
ip = virtualIpv6.getDerivedIpString();
delPtrRecord(aid, lid, ip);
} catch (IPStringConversionException ex) {
String stackTrace = Debug.getEST(ex);
String fmt = "Error while attempting to delete PTR record for"
+ " Virtual IPv6 address for lb %s\n";
String msg = String.format(fmt, rdns.buildDeviceUri(aid, lid));
LOG.error(msg + "Exception: " + stackTrace, ex);
lbe = newDelPtrEvent(aid, lid, "null", msg, stackTrace, true);
loadBalancerRepository.save(lbe);
}
virtualIpv6Repository.deleteVirtualIp(virtualIpv6);
Debug.nop();
}
}
private LoadBalancerServiceEvent newDelPtrEvent(int aid, int lid, String ip, String msg, String detailedMsg, boolean failed) {
LoadBalancerServiceEvent lbe = new LoadBalancerServiceEvent();
lbe.setAccountId(aid);
lbe.setLoadbalancerId(lid);
if (failed) {
lbe.setTitle(DEL_PTR_FAILED);
lbe.setSeverity(EventSeverity.WARNING);
} else {
lbe.setTitle(DEL_PTR_PASSED);
lbe.setSeverity(EventSeverity.INFO);
}
lbe.setDescription(String.format("ip{%s}|%s", ip, msg));
lbe.setType(EventType.DELETE_VIRTUAL_IP);
lbe.setCategory(CategoryType.DELETE);
lbe.setRelativeUri(RdnsHelper.newRdnsHelper(aid).relativeUri(aid, lid));
lbe.setCreated(Calendar.getInstance());
if (detailedMsg != null) {
lbe.setDetailedMessage(detailedMsg);
}
return lbe;
}
@Transactional
private void delPtrRecord(int aid, int lid, String ip) {
RdnsHelper rdns = RdnsHelper.newRdnsHelper(aid);
ClientResponse resp;
String msg;
String fmt;
String respStr;
String lbUrl = rdns.buildDeviceUri(aid, lid);
try {
resp = rdns.delPtrPubRecord(lid, ip);
} catch (Exception ex) {
String stackTrace = Debug.getEST(ex);
msg = "Exception rDNS caught while attempting to delete ptr " + ""
+ "for ip " + ip
+ " on loadbalancer "
+ lbUrl + "\n";
LOG.error(msg + stackTrace, ex);
LoadBalancerServiceEvent lbe = newDelPtrEvent(aid, lid, ip, msg,
"Exception: " + stackTrace, true);
loadBalancerEventRepository.save(lbe);
return;
}
int statusCode = resp.getStatus();
if (statusCode / 100 != 2) {
fmt = "ERROR rDNS returned http status code %d when trying "
+ "attempting to delete PTR record for ip %s for on loadbalancer %s\n";
msg = String.format(fmt, statusCode, ip, lbUrl);
respStr = StaticDNSClientUtils.clientResponseToString(resp);
LOG.error(msg + respStr);
System.out.printf("%s%s\n", msg, respStr);
LoadBalancerServiceEvent lbe = newDelPtrEvent(aid, lid, ip, msg, respStr, true);
loadBalancerEventRepository.save(lbe);
return;
} else {
respStr = StaticDNSClientUtils.clientResponseToString(resp);
fmt = "SUCCESS rDNS DELETING PTR for account=%d loadbalancerId=%d\n%s";
msg = String.format(fmt, aid, lid, respStr);
LoadBalancerServiceEvent lbe = newDelPtrEvent(aid, lid, ip, msg, respStr, false);
LOG.info(msg);
loadBalancerEventRepository.save(lbe);
return;
}
}
private boolean vipTypeMatchesTypeForLoadBalancer(VirtualIpType vipType, LoadBalancer dbLoadBalancer) {
for (LoadBalancerJoinVip loadBalancerJoinVip : dbLoadBalancer.getLoadBalancerJoinVipSet()) {
if (!loadBalancerJoinVip.getVirtualIp().getVipType().equals(vipType)) {
return false;
}
}
return true;
}
private boolean isVipConfiguredOnHost(VirtualIp virtualIp, Host host) {
List<LoadBalancer> loadBalancersUsingVip = virtualIpRepository.getLoadBalancersByVipId(virtualIp.getId());
for (LoadBalancer loadBalancer : loadBalancersUsingVip) {
if (!(loadBalancer.getHost().getName().equals(host.getName()))) {
return false;
}
}
return true;
}
private boolean isIpv6VipConfiguredOnHost(VirtualIpv6 virtualIp, Host host) {
List<LoadBalancer> loadBalancersUsingVip = virtualIpv6Repository.getLoadBalancersByVipId(virtualIp.getId());
for (LoadBalancer loadBalancer : loadBalancersUsingVip) {
if (!(loadBalancer.getHost().getName().equals(host.getName()))) {
return false;
}
}
return true;
}
private boolean isVipPortCombinationInUse(VirtualIp virtualIp, Integer loadBalancerPort) {
return virtualIpRepository.getPorts(virtualIp.getId()).containsKey(loadBalancerPort);
}
@Override
public boolean isIpv4VipPortCombinationInUse(VirtualIp virtualIp, Integer loadBalancerPort) {
return virtualIpRepository.getPorts(virtualIp.getId()).containsKey(loadBalancerPort);
}
@Override
public boolean isIpv6VipPortCombinationInUse(VirtualIpv6 virtualIp, Integer loadBalancerPort) {
return virtualIpv6Repository.getPorts(virtualIp.getId()).containsKey(loadBalancerPort);
}
private boolean testForDuplicatesByCluster(VirtualIp vip, Integer clusterId) {
List<VirtualIp> dbVips = virtualIpRepository.getVipsByClusterId(clusterId);
for (VirtualIp nvip : dbVips) {
if (vip.getIpAddress().equals(nvip.getIpAddress())) {
return true;
}
}
return false;
}
@Override
@Transactional
public Integer getNextVipOctet(Integer accountId) {
return virtualIpv6Repository.getNextVipOctet(accountId);
}
@Override
public VirtualIpv6 newVirtualIpv6(Integer clusterId, Integer accountId, Integer vipOctets) throws EntityNotFoundException {
VirtualIpv6 v6 = new VirtualIpv6();
Cluster c = clusterRepository.getById(clusterId);
v6.setCluster(c);
v6.setAccountId(accountId);
v6.setVipOctets(vipOctets);
return v6;
}
@Override
@Transactional
public VirtualIpv6 allocateIpv6VirtualIp(LoadBalancer loadBalancer) throws EntityNotFoundException {
// Acquire lock on account row due to concurrency issue
virtualIpv6Repository.getLockedAccountRecord(loadBalancer.getAccountId());
VirtualIpv6 v6;
Integer clusterId = loadBalancer.getHost().getCluster().getId();
Integer accountId = loadBalancer.getAccountId();
Integer vipOctets = virtualIpv6Repository.getNextVipOctet(accountId);
v6 = newVirtualIpv6(clusterId, accountId, vipOctets);
virtualIpRepository.persist(v6);
return v6;
}
@Override
@Transactional
public List<Integer> genSha1SumsForAccountTable() throws NoSuchAlgorithmException {
List<Integer> out = new ArrayList<Integer>();
Set<Integer> accountsInAccountTable;
Set<Integer> accountsToHash;
Set<Integer> accountsInLbTable;
accountsInAccountTable = new HashSet<Integer>(virtualIpv6Repository.getAccountIdsAlreadyShaHashed());
accountsInLbTable = new HashSet<Integer>(virtualIpv6Repository.getAccountIds());
accountsToHash = new HashSet<Integer>(accountsInLbTable);
accountsToHash.removeAll(accountsInAccountTable);
for (Integer accountId : accountsToHash) {
Account account = new Account();
String accountIdStr = String.format("%d", accountId);
account.setId(accountId);
account.setSha1SumForIpv6(HashUtil.sha1sumHex(accountIdStr.getBytes(), 0, 4));
persist(account);
out.add(accountId);
}
return out;
}
@Override
@Transactional
public void addAccountRecord(Integer accountId) throws NoSuchAlgorithmException {
Account account;
try {
account = virtualIpv6Repository.getAccountRecordById(accountId);
} catch (EntityNotFoundException ex) {
account = null;
}
if (account == null) {
account = new Account();
String accountIdStr = accountId.toString();
account.setId(accountId);
account.setSha1SumForIpv6(HashUtil.sha1sumHex(accountIdStr.getBytes(), 0, 4));
try {
persist(account);
} catch (Exception e) {
LOG.warn("High concurrency detected. Ignoring...");
}
}
}
@Override
public String getVirtualIpv6String(Integer vip6Id) throws EntityNotFoundException, IPStringConversionException {
VirtualIpv6 v6 = virtualIpv6Repository.getById(vip6Id);
return getVirtualIpv6String(v6);
}
@Override
public String getVirtualIpv6String(VirtualIpv6 vip6) throws IPStringConversionException {
String out;
String clusterCidrString = vip6.getCluster().getClusterIpv6Cidr();
if (clusterCidrString == null) {
String msg = String.format("Cluster[%d] has null value for ClusterIpv6Cider", vip6.getCluster().getId());
throw new IPStringConversionException(msg);
}
IPv6Cidr v6Cidr = new IPv6Cidr(clusterCidrString);
IPv6 v6 = new IPv6("::");
v6.setClusterPartition(v6Cidr);
v6.setAccountPartition(vip6.getAccountId());
v6.setVipOctets(vip6.getVipOctets());
out = v6.expand();
return out;
}
private List<Integer> doesVipsBelongToLoadBalancer(LoadBalancer dbLoadBalancer, List<Integer> virtualIpIdsToDelete) {
return ListUtil.compare(virtualIpIdsToDelete, getVipIdsInDb(dbLoadBalancer));
}
private List<Integer> getVipIdsInDb(LoadBalancer loadBalancer) {
List<Integer> vipIdsInDb = new ArrayList<Integer>();
for (LoadBalancerJoinVip loadBalancerJoinVip : loadBalancer.getLoadBalancerJoinVipSet()) {
vipIdsInDb.add(loadBalancerJoinVip.getVirtualIp().getId());
}
for (LoadBalancerJoinVip6 loadBalancerJoinVip6 : loadBalancer.getLoadBalancerJoinVip6Set()) {
vipIdsInDb.add(loadBalancerJoinVip6.getVirtualIp().getId());
}
return vipIdsInDb;
}
@Override
public Map<Integer, List<VirtualIp>> getAllocatedVipsMappedByLbId() {
Map<Integer, List<VirtualIp>> vipMap = new HashMap<Integer, List<VirtualIp>>();
List<VirtualIp> vips = virtualIpRepository.getAll();
for (VirtualIp vip : vips) {
if (vip.getLoadBalancerJoinVipSet().size() == 0) {
continue;
}
Integer lbId = vip.getLoadBalancerJoinVipSet().iterator().next().getLoadBalancer().getId();
if (vip.isAllocated() && vip.getLoadBalancerJoinVipSet() != null
&& !vip.getLoadBalancerJoinVipSet().isEmpty()) {
if (!vipMap.containsKey(lbId)) {
vipMap.put(lbId, new ArrayList<VirtualIp>());
}
vipMap.get(lbId).add(vip);
}
}
return vipMap;
}
@Override
public Account getAccountRecord(Integer aid) throws EntityNotFoundException {
return virtualIpv6Repository.getAccountRecordById(aid);
}
@Override
@Transactional
public Account updateOrCreateAccountRecord(Account apiAccount) throws NoSuchAlgorithmException, EntityNotFoundException {
Account dbAccount;
if (apiAccount.getId() == null) {
throw new EntityNotFoundException("please specify an account id as the account table does not have an auto incrementing primary key");
}
try {
dbAccount = virtualIpv6Repository.getAccountRecordById(apiAccount.getId());
} catch (EntityNotFoundException ex) {
dbAccount = new Account();
dbAccount.setId(apiAccount.getId());
dbAccount.setSha1SumForIpv6(HashUtil.sha1sumHex(apiAccount.getId().toString().getBytes(), 0, 4));
virtualIpv6Repository.persist(dbAccount);
}
if (apiAccount.getSha1SumForIpv6() != null) {
dbAccount.setSha1SumForIpv6(apiAccount.getSha1SumForIpv6());
}
if (apiAccount.getClusterType() != null) {
dbAccount.setClusterType(apiAccount.getClusterType());
}
virtualIpv6Repository.merge(dbAccount);
virtualIpv6Repository.flush();
return virtualIpv6Repository.getAccountRecordById(dbAccount.getId()); // Incase the merge entity isn't in the same persistence context
}
@Override
@Transactional
public boolean deleteAccountRecord(Integer aid) {
Account account;
if (aid == null) {
return false;
}
try {
account = virtualIpv6Repository.getAccountRecordById(aid);
} catch (EntityNotFoundException ex) {
return false; //Nothing to be deleted
}
virtualIpRepository.remove(account);
return true;
}
}