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.AccountLimitType;
import org.openstack.atlas.service.domain.entities.Node;
import org.openstack.atlas.service.domain.entities.NodeMeta;
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.NodeMetadataService;
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 java.util.*;
@Service
public class NodeMetadataServiceImpl extends BaseService implements NodeMetadataService {
private final Log LOG = LogFactory.getLog(NodeMetadataServiceImpl.class);
@Autowired
private AccountLimitService accountLimitService;
@Override
public List<NodeMeta> createNodeMetadata(Integer accountId, Integer loadbalancerId, Integer nodeId, List<NodeMeta> nodeMetadata) throws EntityNotFoundException, ImmutableEntityException, UnprocessableEntityException, BadRequestException {
Node node;
try {
node = nodeRepository.getNodeByAccountIdLoadBalancerIdNodeId(accountId, loadbalancerId, nodeId);
} catch(Exception e) {
throw new EntityNotFoundException(String.format("Node with id %d doesn't exist", nodeId));
}
try {
isLbActive(node.getLoadbalancer());
} catch (Exception e) {
throw new BadRequestException(String.format("Loadbalancer %d is deleted, therefore it is immutable.", loadbalancerId));
}
try {
Integer potentialTotalNumMetas = node.getNodeMetadata().size() + nodeMetadata.size();
Integer metaLimit = accountLimitService.getLimit(accountId, AccountLimitType.NODE_META_LIMIT);
LOG.debug(String.format("Verifying that metadata limit isn't reached for node '%d'...", node.getId()));
if (potentialTotalNumMetas > metaLimit) {
throw new BadRequestException(String.format("Metadata must not exceed %d per node.", 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 node '%d'...", nodeId));
if (detectDuplicateNodeMetadata(node.getNodeMetadata(), nodeMetadata)) {
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 node.");
}
LOG.debug(String.format("Current number of metadata items for node '%d': %d", nodeId, node.getNodeMetadata().size()));
LOG.debug(String.format("Number of new metadata items to be added: %d", nodeMetadata.size()));
List<NodeMeta> createdNodeMeta = nodeMetadataRepository.addNodeMetas(node, nodeMetadata);
try {
LOG.debug(String.format("Successfully added %d metadata items for node '%d'", createdNodeMeta.size(), nodeId));
} catch (NullPointerException e) {
LOG.debug(String.format("No metadata items to add for node '%d'", 0, nodeId));
}
return createdNodeMeta;
}
@Override
public List<NodeMeta> getNodeMetadataByAccountIdNodeId(Integer accountId, Integer nodeId) throws EntityNotFoundException {
return nodeMetadataRepository.getNodeMetaDataByAccountIdNodeId(nodeId);
}
@Override
public List<String> prepareForNodeMetadataDeletion(Integer accountId, Integer loadbalancerId, Integer nodeId, List<Integer> ids) throws EntityNotFoundException {
List<String> validationErrors = new ArrayList<String>();
List<Integer> nodeMetaIds = new ArrayList<Integer>();
List<Integer> invalidIds = new ArrayList<Integer>();
String format, errorMessage;
Node currentNode;
try {
currentNode = nodeRepository.getNodeByAccountIdLoadBalancerIdNodeId(accountId, loadbalancerId, nodeId);
} catch (Exception e) {
validationErrors.add("Could not find node");
return validationErrors;
}
for (NodeMeta meta : currentNode.getNodeMetadata()) {
nodeMetaIds.add(meta.getId());
}
for (Integer id : ids) {
if (!nodeMetaIds.contains(id)) {
invalidIds.add(id);
}
}
int batchDeleteLimit = accountLimitService.getLimit(accountId, AccountLimitType.BATCH_DELETE_LIMIT);
if (ids.size() > batchDeleteLimit) {
format = "Request to delete %d metadata items exceeds the account batch delete limit.\n" +
"BATCH_DELETE_LIMIT is %d";
errorMessage = String.format(format, ids.size(), batchDeleteLimit);
validationErrors.add(errorMessage);
}
if (!invalidIds.isEmpty()) {
format = "NodeMetaData ids %s are not part of your node.";
errorMessage = String.format(format, StringConverter.integersAsString(invalidIds));
validationErrors.add(errorMessage);
}
return validationErrors;
}
@Override
public void deleteNodeMetadata(Node node, Collection<Integer> ids) throws EntityNotFoundException {
nodeMetadataRepository.deleteMetadata(node, ids);
}
@Override
public NodeMeta getNodeMeta(Integer nodeId, Integer id) throws EntityNotFoundException {
try {
return nodeMetadataRepository.getNodeMeta(nodeId, id);
} catch (Exception e) {
throw new EntityNotFoundException("Node meta data " + id + " doesn't exist on node " + nodeId + ".");
}
}
@Override
public NodeMeta updateNodeMeta(Integer accountId, Integer loadbalancerId, Integer nodeId, NodeMeta callNodeMeta) throws EntityNotFoundException {
Node node = nodeRepository.getNodeByAccountIdLoadBalancerIdNodeId(accountId, loadbalancerId, nodeId);
List<Integer> ids = new ArrayList<Integer>();
for (NodeMeta nodeMeta : node.getNodeMetadata()) {
ids.add(nodeMeta.getId());
if (nodeMeta.getId().equals(callNodeMeta.getId())) {
callNodeMeta.setKey(nodeMeta.getKey());
}
}
if (!ids.contains(callNodeMeta.getId())) {
throw new EntityNotFoundException("Node meta with id " + callNodeMeta.getId() + " doesn't exist.");
} else {
callNodeMeta.setNode(node);
}
return nodeMetadataRepository.updateNodeMeta(node, callNodeMeta);
}
private boolean detectDuplicateNodeMetadata(Collection<NodeMeta> nodemeta1, Collection<NodeMeta> nodemeta2) {
Set<String> keys = new HashSet<String>();
for (NodeMeta meta : nodemeta1) {
if (!keys.add(meta.getKey())) {
return true;
}
}
for (NodeMeta meta : nodemeta2) {
if (!keys.add(meta.getKey())) {
return true;
}
}
return false;
}
}