package org.openstack.atlas.service.domain.repository;
import javassist.tools.rmi.ObjectNotFoundException;
import org.openstack.atlas.lb.helpers.ipstring.IPv4Range;
import org.openstack.atlas.lb.helpers.ipstring.IPv4Ranges;
import org.openstack.atlas.lb.helpers.ipstring.IPv4ToolSet;
import org.openstack.atlas.lb.helpers.ipstring.exceptions.IPStringException;
import org.openstack.atlas.service.domain.entities.*;
import org.openstack.atlas.service.domain.exceptions.EntityNotFoundException;
import org.openstack.atlas.service.domain.exceptions.OutOfVipsException;
import org.openstack.atlas.service.domain.pojos.VirtualIpBlock;
import org.openstack.atlas.service.domain.pojos.VirtualIpBlocks;
import org.openstack.atlas.service.domain.util.Constants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.*;
import javax.persistence.criteria.*;
import java.math.BigInteger;
import java.util.*;
@Repository
@Transactional
public class VirtualIpRepository {
private final Log LOG = LogFactory.getLog(VirtualIpRepository.class);
@PersistenceContext(unitName = "loadbalancing")
private EntityManager entityManager;
public VirtualIpRepository() {
}
public VirtualIpRepository(EntityManager em) {
this.entityManager = em;
}
public VirtualIpv6 getVirtualIpv6BytClusterAccountOctet(Integer cid, Integer aid, Integer vo) throws EntityNotFoundException {
List<VirtualIpv6> vips = new ArrayList<VirtualIpv6>();
if (cid == null) {
throw new EntityNotFoundException("Cluster not found");
}
if (aid == null) {
throw new EntityNotFoundException("Account not found");
}
if (vo == null) {
throw new EntityNotFoundException("vipOctets is null");
}
String qStr = "select v from VirtualIpv6 v where v.cluster.id=:cid and v.accountId=:aid and v.vipOctets=:vo";
Query q = entityManager.createQuery(qStr).setParameter("cid", cid).setParameter("aid", aid).setParameter("vo", vo);
vips = q.getResultList();
if (vips.isEmpty()) {
throw new EntityNotFoundException("Vip not found");
}
return vips.get(0);
}
public List<VirtualIp> listFreeVirtualIps() throws OutOfVipsException {
List<VirtualIp> vips;
VirtualIp freeVip;
LOG.info("About to execute query for 'getAvailableVirtualIp()'");
String query = "select v from VirtualIp v where v.lastDeallocation > v.lastAllocation or v.last_allocation is null"
+ "order by v.lastDeallocation asc";
vips = entityManager.createQuery(query).getResultList();
if (vips.isEmpty()) {
LOG.error("No available virtual ips.");
throw new OutOfVipsException("No available virtual ips. Please contact support.");
}
return vips;
}
public List<VirtualIp> getAll() {
return getAll(null);
}
public Integer getNumUniqueVipsForAccount(Integer accountId, VirtualIpType type) {
Query query = entityManager.createNativeQuery("select count(distinct j.virtualip_id) from loadbalancer_virtualip j, loadbalancer l, virtual_ip_ipv4 v where l.id = j.loadbalancer_id and v.id = j.virtualip_id and v.type = :virtualIpType and l.account_id = :accountId").setParameter("virtualIpType", type.toString()).setParameter("accountId", accountId.toString());
return ((BigInteger) query.getSingleResult()).intValue();
}
public List<VirtualIp> getVipsByLoadBalancerId(Integer loadBalancerId) {
List<VirtualIp> vips;
String query = "select j.virtualIp from LoadBalancerJoinVip j where j.loadBalancer.id = :loadBalancerId";
// String query = "select l.virtualIps from LoadBalancer l where l.id = :loadBalancerId";
vips = entityManager.createQuery(query).setParameter("loadBalancerId", loadBalancerId).getResultList();
return vips;
}
public List<VirtualIp> getVipsByClusterId(Integer clusterId) {
List<VirtualIp> vips;
String query = "select j from VirtualIp j where j.cluster.id = :clusterId";
vips = entityManager.createQuery(query).setParameter("clusterId", clusterId).getResultList();
return vips;
}
public List<VirtualIp> getVipsByAccountId(Integer accountId) {
List<VirtualIp> vips;
String query = "select distinct(j.virtualIp) from LoadBalancerJoinVip j where j.loadBalancer.accountId = :accountId";
vips = entityManager.createQuery(query).setParameter("accountId", accountId).getResultList();
return vips;
}
public List<LoadBalancer> getLoadBalancerByVip6Address(Integer vip6Id) {
List<LoadBalancer> loadBalancers;
String query = "select j.loadBalancer from LoadBalancerJoinVip6 j where j.virtualIp.id=:vipId";
loadBalancers = entityManager.createQuery(query).setParameter("vipId", vip6Id).getResultList();
return loadBalancers;
}
public List<LoadBalancer> getLoadBalancersByVipId(Integer vipId) {
List<LoadBalancer> loadBalancers;
String query = "select v.loadBalancer from LoadBalancerJoinVip v where v.virtualIp.id = :vipId";
loadBalancers = entityManager.createQuery(query).setParameter("vipId", vipId).getResultList();
return loadBalancers;
}
public List<LoadBalancer> getLoadBalancerByVipAddress(String address) {
List<LoadBalancer> loadBalancers;
Integer vipId;
String query = "select v.id from VirtualIp v where v.ipAddress = :address";
try {
vipId = (Integer) entityManager.createQuery(query).setParameter("address", address).getSingleResult();
} catch (NoResultException e) {
return new ArrayList<LoadBalancer>();
}
loadBalancers = getLoadBalancersByVipId(vipId);
return loadBalancers;
}
public List<VirtualIp> getAll(String orderBy, Integer... p) {
List<VirtualIp> vips;
String queryStr = "from VirtualIp v left join fetch v.loadBalancerJoinVipSet group by v";
if (orderBy != null) {
queryStr = String.format("%s order by v.%s", queryStr, orderBy);
}
Query query = entityManager.createQuery(queryStr);
if (p.length >= 2) {
Integer offset = p[0];
Integer limit = p[1];
if (offset == null) {
offset = 0;
}
if (limit == null) {
limit = 100;
}
query = query.setFirstResult(offset).setMaxResults(limit);
}
vips = query.getResultList();
return vips;
}
//moved this to cluster repository
public Cluster getClusterById(Integer id) throws EntityNotFoundException {
List<Cluster> cl = entityManager.createQuery("from Cluster c where c.id = :id").setParameter("id",
id).getResultList();
if (cl.isEmpty()) {
String errMsg = String.format("Cannot access cluster {id=%d}", id);
LOG.warn(errMsg);
throw new EntityNotFoundException(errMsg);
}
return cl.get(0);
}
public List<LoadBalancer> getLbsByVirtualIp4Blocks(VirtualIpBlocks vblocks) throws IPStringException {
List<LoadBalancer> out = new ArrayList<LoadBalancer>();
IPv4Ranges ipv4Ranges = ip4Blocks2ip4Ranges(vblocks);
List<Object> hResults;
LoadBalancer lb;
String qStr = "select j.virtualIp.ipAddress, j.virtualIp.id, j.virtualIp.accountId, j.loadBalancer.port, j.virtualIp.ipVersion, j.virtualIp.vipType "
+ "from LoadBalancerJoinVip j order by j.loadBalancer.accountId, j.loadBalancer.id, j.loadBalancer.port, j.virtualIp.id";
// String qStr = "select v.ipAddress,v.id,l.id,l.accountId,l.port, "
// + "v.ipVersion, v.vipType "
// + "from VirtualIp v join v.loadBalancers l "
// + "order by l.accountId,l.id,l.port,v.id";
hResults = entityManager.createQuery(qStr).getResultList();
Integer clid = -1;
lb = new LoadBalancer();
for (Object r : hResults) {
Object[] row = (Object[]) r;
String ip = (String) row[0];
Integer vid = (Integer) row[1];
Integer lid = (Integer) row[2];
Integer aid = (Integer) row[3];
Integer port = (Integer) row[4];
IpVersion ipVersion = (IpVersion) row[5];
VirtualIpType vType = (VirtualIpType) row[6];
if (ipv4Ranges.contains(ip)) {
if (clid != lid) {
clid = lid;
lb = new LoadBalancer();
lb.setAccountId(aid);
lb.setId(lid);
lb.setPort(port);
lb.setLoadBalancerJoinVipSet(new HashSet<LoadBalancerJoinVip>());
out.add(lb);
}
VirtualIp vip = new VirtualIp();
vip.setIpAddress(ip);
vip.setId(vid);
vip.setVipType(vType);
LoadBalancerJoinVip loadBalancerJoinVip = new LoadBalancerJoinVip(lb.getPort(), lb, vip);
lb.getLoadBalancerJoinVipSet().add(loadBalancerJoinVip);
}
}
return out;
}
public void persist(Object obj) {
entityManager.persist(obj);
}
public void merge(Object obj) {
entityManager.merge(obj);
}
public void remove(Object obj) {
entityManager.remove(obj);
}
public void refresh(Object obj) {
entityManager.refresh(obj);
}
//ToDo: for Debugging only remove when in production
public EntityManager getEntityManager() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public String getStackTrace(Exception ex) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Exception: %s:%s\n", ex.getMessage(), ex.getClass().getName()));
for (StackTraceElement se : ex.getStackTrace()) {
sb.append(String.format("%s\n", se.toString()));
}
return sb.toString();
}
private IPv4Ranges ip4Blocks2ip4Ranges(VirtualIpBlocks vBlocks) throws IPStringException {
long lo;
long hi;
IPv4Ranges ipv4Ranges = new IPv4Ranges();
for (VirtualIpBlock vBlock : vBlocks.getVirtualIpBlocks()) {
IPv4Range ipv4Range = new IPv4Range();
lo = IPv4ToolSet.ip2long(vBlock.getFirstIp());
hi = IPv4ToolSet.ip2long(vBlock.getLastIp());
ipv4Range.setLo(lo);
ipv4Range.setHi(hi);
ipv4Ranges.add(ipv4Range);
}
return ipv4Ranges;
}
public VirtualIp allocateIpv4VipBeforeDate(Cluster cluster, Calendar vipReuseTime, VirtualIpType vipType) throws OutOfVipsException {
VirtualIp vipCandidate;
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<VirtualIp> criteria = builder.createQuery(VirtualIp.class);
Root<VirtualIp> vipRoot = criteria.from(VirtualIp.class);
Predicate isNotAllocated = builder.equal(vipRoot.get(VirtualIp_.isAllocated), false);
Predicate lastDeallocationIsNull = builder.isNull(vipRoot.get(VirtualIp_.lastDeallocation));
Predicate isBeforeLastDeallocation = builder.lessThan(vipRoot.get(VirtualIp_.lastDeallocation), vipReuseTime);
Predicate isVipType = builder.equal(vipRoot.get(VirtualIp_.vipType), vipType);
Predicate belongsToCluster = builder.equal(vipRoot.get(VirtualIp_.cluster), cluster);
criteria.select(vipRoot);
criteria.where(builder.and(isNotAllocated, isVipType, belongsToCluster, builder.or(lastDeallocationIsNull, isBeforeLastDeallocation)));
try {
vipCandidate = entityManager.createQuery(criteria).setLockMode(LockModeType.PESSIMISTIC_WRITE).setMaxResults(1).getSingleResult();
} catch (Exception e) {
LOG.error(e);
throw new OutOfVipsException(Constants.OutOfVips);
}
vipCandidate.setAllocated(true);
vipCandidate.setLastAllocation(Calendar.getInstance());
entityManager.merge(vipCandidate);
return vipCandidate;
}
public VirtualIp allocateIpv4VipAfterDate(Cluster cluster, Calendar vipReuseTime, VirtualIpType vipType) throws OutOfVipsException {
VirtualIp vipCandidate;
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<VirtualIp> criteria = builder.createQuery(VirtualIp.class);
Root<VirtualIp> vipRoot = criteria.from(VirtualIp.class);
Predicate isNotAllocated = builder.equal(vipRoot.get(VirtualIp_.isAllocated), false);
Predicate isAfterLastDeallocation = builder.greaterThan(vipRoot.get(VirtualIp_.lastDeallocation), vipReuseTime);
Predicate isVipType = builder.equal(vipRoot.get(VirtualIp_.vipType), vipType);
Predicate belongsToCluster = builder.equal(vipRoot.get(VirtualIp_.cluster), cluster);
criteria.select(vipRoot);
criteria.where(isNotAllocated, isAfterLastDeallocation, isVipType, belongsToCluster);
try {
vipCandidate = entityManager.createQuery(criteria).setLockMode(LockModeType.PESSIMISTIC_WRITE).setMaxResults(1).getSingleResult();
} catch (Exception e) {
LOG.error(e);
throw new OutOfVipsException(Constants.OutOfVips);
}
vipCandidate.setAllocated(true);
vipCandidate.setLastAllocation(Calendar.getInstance());
entityManager.merge(vipCandidate);
return vipCandidate;
}
public void deallocateVirtualIp(VirtualIp virtualIp) {
virtualIp = entityManager.find(VirtualIp.class, virtualIp.getId());
virtualIp.setAllocated(false);
virtualIp.setLastDeallocation(Calendar.getInstance());
entityManager.merge(virtualIp);
LOG.info(String.format("Virtual Ip '%d' de-allocated.", virtualIp.getId()));
}
public List<LoadBalancerJoinVip> getJoinRecordsForVip(VirtualIp virtualIp) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<LoadBalancerJoinVip> criteria = builder.createQuery(LoadBalancerJoinVip.class);
Root<LoadBalancerJoinVip> lbJoinVipRoot = criteria.from(LoadBalancerJoinVip.class);
Predicate hasVip = builder.equal(lbJoinVipRoot.get(LoadBalancerJoinVip_.virtualIp), virtualIp);
criteria.select(lbJoinVipRoot);
criteria.where(hasVip);
return entityManager.createQuery(criteria).setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();
}
public Long getNumLoadBalancersAttachedToVip(VirtualIp virtualIp) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
Root<LoadBalancerJoinVip> lbJoinVipRoot = criteria.from(LoadBalancerJoinVip.class);
Expression countExp = builder.count(lbJoinVipRoot.get(LoadBalancerJoinVip_.loadBalancer));
Predicate hasVip = builder.equal(lbJoinVipRoot.get(LoadBalancerJoinVip_.virtualIp), virtualIp);
criteria.select(countExp);
criteria.where(hasVip);
return entityManager.createQuery(criteria).setLockMode(LockModeType.PESSIMISTIC_WRITE).getSingleResult();
}
public Long getNumIpv4VipsForLoadBalancer(LoadBalancer loadBalancer) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
Root<LoadBalancerJoinVip> lbJoinVipRoot = criteria.from(LoadBalancerJoinVip.class);
Expression countExp = builder.count(lbJoinVipRoot.get(LoadBalancerJoinVip_.virtualIp));
Predicate hasVip = builder.equal(lbJoinVipRoot.get(LoadBalancerJoinVip_.loadBalancer), loadBalancer);
criteria.select(countExp);
criteria.where(hasVip);
return entityManager.createQuery(criteria).setLockMode(LockModeType.PESSIMISTIC_WRITE).getSingleResult();
}
public void removeJoinRecord(LoadBalancerJoinVip loadBalancerJoinVip) {
loadBalancerJoinVip = entityManager.find(LoadBalancerJoinVip.class, loadBalancerJoinVip.getId());
VirtualIp virtualIp = entityManager.find(VirtualIp.class, loadBalancerJoinVip.getVirtualIp().getId());
virtualIp.getLoadBalancerJoinVipSet().remove(loadBalancerJoinVip);
entityManager.remove(loadBalancerJoinVip);
}
public void deleteVirtualIp(VirtualIp virtualIp) {
entityManager.remove(virtualIp);
}
public VirtualIp getById(Integer id) throws EntityNotFoundException {
VirtualIp virtualIp = entityManager.find(VirtualIp.class, id);
if (virtualIp == null) {
String message = Constants.VirtualIpNotFound;
LOG.warn(message);
throw new EntityNotFoundException(message);
}
return virtualIp;
}
public List<Integer> getAccountIds(VirtualIp virtualIp) {
List<Integer> accountIds;
String query = "select distinct(j.loadBalancer.accountId) from LoadBalancerJoinVip j where j.virtualIp.id = :vipId order by (j.loadBalancer.accountId)";
accountIds = entityManager.createQuery(query).setParameter("vipId", virtualIp.getId()).getResultList();
return accountIds;
}
public List<Integer> getAccountBySha1Sum(String sha1) {
List<Integer> accountIds;
String qStr = "select a.id from Account a where a.sha1SumForIpv6=:sha";
accountIds = entityManager.createQuery(qStr).setParameter("sha", sha1).getResultList();
return accountIds;
}
public Map<Integer, List<LoadBalancer>> getPorts(Integer vid) {
Map<Integer, List<LoadBalancer>> map = new TreeMap<Integer, List<LoadBalancer>>();
List<Object> hResults;
String query = "select j.virtualIp.id, j.loadBalancer.id, j.loadBalancer.accountId, j.loadBalancer.port "
+ "from LoadBalancerJoinVip j where j.virtualIp.id = :vid order by j.loadBalancer.port, j.loadBalancer.id";
hResults = entityManager.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_READ)
.setParameter("vid", vid).getResultList();
for (Object r : hResults) {
Object[] row = (Object[]) r;
Integer port = (Integer) row[3];
if (!map.containsKey(port)) {
map.put(port, new ArrayList<LoadBalancer>());
}
LoadBalancer lb = new LoadBalancer();
lb.setId((Integer) row[1]);
lb.setAccountId((Integer) row[2]);
lb.setPort((Integer) row[3]);
map.get(port).add(lb);
int sslPort = getSslPorts((Integer) row[1]);
if ((sslPort != -1) && !map.containsKey(sslPort)) {
map.put(sslPort, new ArrayList<LoadBalancer>());
LoadBalancer lbssl = new LoadBalancer();
lbssl.setId((Integer) row[1]);
lbssl.setAccountId((Integer) row[2]);
lbssl.setPort((Integer) row[3]);
map.get(sslPort).add(lbssl);
}
}
return map;
}
public int getSslPorts(Integer lbId) {
Map<Integer, List<LoadBalancer>> map = new TreeMap<Integer, List<LoadBalancer>>();
List<Object> hResults;
String query = "select j.securePort "
+ "from SslTermination j where j.loadbalancer.id = :lbId";
hResults = entityManager.createQuery(query).setParameter("lbId", lbId).getResultList();
if (hResults == null || hResults.isEmpty()) {
return -1;
}
return (Integer) hResults.get(0);
}
}