package org.openstack.atlas.service.domain.services.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openstack.atlas.service.domain.entities.*;
import org.openstack.atlas.service.domain.exceptions.BadRequestException;
import org.openstack.atlas.service.domain.exceptions.EntityNotFoundException;
import org.openstack.atlas.service.domain.exceptions.ImmutableEntityException;
import org.openstack.atlas.service.domain.exceptions.UnprocessableEntityException;
import org.openstack.atlas.service.domain.services.AccountLimitService;
import org.openstack.atlas.service.domain.services.LoadbalancerMetadataService;
import org.openstack.atlas.util.converters.StringConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Service
public class LoadbalancerMetadataServiceImpl extends BaseService implements LoadbalancerMetadataService {
private final Log LOG = LogFactory.getLog(LoadbalancerMetadataServiceImpl.class);
@Autowired
private AccountLimitService accountLimitService;
@Override
public Set<LoadbalancerMeta> createLoadbalancerMetadata(Integer accountId, Integer loadBalancerId, Collection<LoadbalancerMeta> loadbalancerMetas) throws EntityNotFoundException, ImmutableEntityException, UnprocessableEntityException, BadRequestException {
LoadBalancer oldLb = loadBalancerRepository.getByIdAndAccountId(loadBalancerId, accountId);
isLbActive(oldLb);
try {
Integer potentialTotalNumMetas = oldLb.getLoadbalancerMetadata().size() + loadbalancerMetas.size();
Integer metaLimit = accountLimitService.getLimit(oldLb.getAccountId(), AccountLimitType.LOADBALANCER_META_LIMIT);
LOG.debug(String.format("Verifying that metadata limit isn't reached for lb '%d'...", loadBalancerId));
if (potentialTotalNumMetas > metaLimit) {
throw new BadRequestException(String.format("Metadata must not exceed %d per load balancer.", metaLimit));
}
} catch (EntityNotFoundException e) {
LOG.warn("No metadata limit found! The user can add as many metadata items as they want!");
}
LOG.debug(String.format("Verifying that there are no duplicate metadata keys for lb '%d'...", loadBalancerId));
if (detectDuplicateMetadata(oldLb.getLoadbalancerMetadata(), loadbalancerMetas)) {
LOG.warn("Duplicate metadata keys found! Sending failure response back to client...");
throw new UnprocessableEntityException("Duplicate metadata keys detected. One or more metadata keys already configured on load balancer.");
}
LOG.debug(String.format("Current number of metadata items for loadbalancer '%d': %d", loadBalancerId, oldLb.getLoadbalancerMetadata().size()));
LOG.debug(String.format("Number of new metadata items to be added: %d", loadbalancerMetas.size()));
final Set<LoadbalancerMeta> loadbalancerMetaSet = loadbalancerMetadataRepository.addLoadbalancerMetas(oldLb, loadbalancerMetas);
LOG.debug(String.format("Successfully added %d metadata items for loadbalancer '%d'", loadbalancerMetaSet.size(), loadBalancerId));
return loadbalancerMetaSet;
}
@Override
public Set<LoadbalancerMeta> getLoadbalancerMetadataByAccountIdLoadBalancerId(Integer accountId, Integer loadBalancerId) throws EntityNotFoundException {
final List<LoadbalancerMeta> metadataByAccountIdLoadBalancerId = loadbalancerMetadataRepository.getLoadbalancerMetadataByAccountIdLoadBalancerId(accountId, loadBalancerId);
Set<LoadbalancerMeta> loadbalancerMetaSet = new HashSet<LoadbalancerMeta>();
for (LoadbalancerMeta loadbalancerMeta : metadataByAccountIdLoadBalancerId) {
loadbalancerMetaSet.add(loadbalancerMeta);
}
return loadbalancerMetaSet;
}
@Override
public LoadbalancerMeta getLoadbalancerMeta(Integer accountId, Integer loadBalancerId, Integer id) throws EntityNotFoundException {
return loadbalancerMetadataRepository.getLoadbalancerMeta(accountId, loadBalancerId, id);
}
@Override
public void deleteLoadbalancerMeta(Integer accountId, Integer loadBalancerId, Integer id) throws EntityNotFoundException {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(loadBalancerId, accountId);
loadbalancerMetadataRepository.deleteLoadbalancerMeta(dbLoadBalancer, id);
}
@Override
public void updateLoadbalancerMeta(LoadBalancer msgLb) throws EntityNotFoundException {
LoadBalancer currentLb = loadBalancerRepository.getByIdAndAccountId(msgLb.getId(), msgLb.getAccountId());
LoadbalancerMeta loadbalancerMetaToUpdate = msgLb.getLoadbalancerMetadata().iterator().next();
if (!loadBalancerContainsMeta(currentLb, loadbalancerMetaToUpdate)) {
LOG.warn("LoadbalancerMeta to update not found. Sending response to client...");
throw new EntityNotFoundException(String.format("LoadbalancerMeta data item with id #%d not found for loadbalancer #%d", loadbalancerMetaToUpdate.getId(), msgLb.getId()));
}
LOG.debug("LoadbalancerMeta on dbLoadbalancer: " + currentLb.getLoadbalancerMetadata().size());
for (LoadbalancerMeta loadbalancerMeta : currentLb.getLoadbalancerMetadata()) {
if (loadbalancerMeta.getId().equals(loadbalancerMetaToUpdate.getId())) {
LOG.info("LoadbalancerMeta to be updated found: " + loadbalancerMeta.getId());
if (loadbalancerMetaToUpdate.getKey() != null) {
loadbalancerMeta.setKey(loadbalancerMetaToUpdate.getKey());
}
if (loadbalancerMetaToUpdate.getValue() != null) {
loadbalancerMeta.setValue(loadbalancerMetaToUpdate.getValue());
}
break;
}
}
loadbalancerMetadataRepository.update(currentLb);
}
@Override
public List<String> prepareForLoadbalancerMetadataDeletion(Integer accountId, Integer loadBalancerId, List<Integer> ids) throws EntityNotFoundException {
List<String> validationErrors = new ArrayList<String>();
String format, errMsg;
LoadBalancer currentLb = loadBalancerRepository.getByIdAndAccountId(loadBalancerId, accountId);
Set<Integer> currentMetaIds = new HashSet<Integer>();
Set<Integer> invalidMetaIds = new HashSet<Integer>();
for (LoadbalancerMeta loadbalancerMeta : currentLb.getLoadbalancerMetadata()) {
currentMetaIds.add(loadbalancerMeta.getId());
}
for (Integer id : ids) {
if(!currentMetaIds.contains(id)) invalidMetaIds.add(id);
}
int batch_delete_limit = accountLimitService.getLimit(accountId, AccountLimitType.BATCH_DELETE_LIMIT);
if (ids.size() > batch_delete_limit) {
format = "Request to delete %d metadata items exceeds the account limit"
+ " BATCH_DELETE_LIMIT of %d please attempt to delete fewer then %d nodes";
errMsg = String.format(format, ids.size(), batch_delete_limit, batch_delete_limit);
validationErrors.add(errMsg);
}
if (!invalidMetaIds.isEmpty()) {
// Don't even take this request seriously any ID does not belong to this account
format = "Metadata ids %s are not a part of your loadbalancer";
errMsg = String.format(format, StringConverter.integersAsString(invalidMetaIds));
validationErrors.add(errMsg);
}
return validationErrors;
}
@Transactional
@Override
public LoadBalancer deleteMetadata(LoadBalancer lb, Collection<Integer> ids) throws EntityNotFoundException {
LoadBalancer dbLoadBalancer = loadBalancerRepository.getByIdAndAccountId(lb.getId(), lb.getAccountId());
return loadbalancerMetadataRepository.deleteLoadbalancerMetadata(dbLoadBalancer, ids);
}
private boolean loadBalancerContainsMeta(LoadBalancer lb, LoadbalancerMeta loadbalancerMeta) {
for (LoadbalancerMeta m : lb.getLoadbalancerMetadata()) {
if (m.getId().equals(loadbalancerMeta.getId())) {
return true;
}
}
return false;
}
private boolean detectDuplicateMetadata(Collection<LoadbalancerMeta> metadata1, Collection<LoadbalancerMeta> metadata2) {
Set<String> keys = new HashSet<String>();
for (LoadbalancerMeta loadbalancerMeta : metadata1) {
if (!keys.add(loadbalancerMeta.getKey())) {
return true;
}
}
for (LoadbalancerMeta loadbalancerMeta : metadata2) {
if (!keys.add(loadbalancerMeta.getKey())) {
return true;
}
}
return false;
}
}