package com.sequenceiq.cloudbreak.core.flow2.cluster.upscale;
import static com.sequenceiq.cloudbreak.api.model.Status.AVAILABLE;
import static com.sequenceiq.cloudbreak.api.model.Status.UPDATE_FAILED;
import static com.sequenceiq.cloudbreak.api.model.Status.UPDATE_IN_PROGRESS;
import static com.sequenceiq.cloudbreak.common.type.CloudConstants.BYOS;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.sequenceiq.cloudbreak.api.model.DetailedStackStatus;
import com.sequenceiq.cloudbreak.api.model.InstanceStatus;
import com.sequenceiq.cloudbreak.common.type.HostMetadataState;
import com.sequenceiq.cloudbreak.core.flow2.stack.FlowMessageService;
import com.sequenceiq.cloudbreak.core.flow2.stack.Msg;
import com.sequenceiq.cloudbreak.domain.HostGroup;
import com.sequenceiq.cloudbreak.domain.HostMetadata;
import com.sequenceiq.cloudbreak.domain.Stack;
import com.sequenceiq.cloudbreak.repository.HostMetadataRepository;
import com.sequenceiq.cloudbreak.repository.StackUpdater;
import com.sequenceiq.cloudbreak.service.cluster.ClusterService;
import com.sequenceiq.cloudbreak.service.cluster.flow.EmailSenderService;
import com.sequenceiq.cloudbreak.service.hostgroup.HostGroupService;
import com.sequenceiq.cloudbreak.service.stack.StackService;
import com.sequenceiq.cloudbreak.util.StackUtil;
@Component
public class ClusterUpscaleFlowService {
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterUpscaleFlowService.class);
@Inject
private StackService stackService;
@Inject
private ClusterService clusterService;
@Inject
private FlowMessageService flowMessageService;
@Inject
private EmailSenderService emailSenderService;
@Inject
private StackUpdater stackUpdater;
@Inject
private HostGroupService hostGroupService;
@Inject
private HostMetadataRepository hostMetadataRepository;
@Inject
private StackUtil stackUtil;
public void upscalingAmbari(Stack stack) {
clusterService.updateClusterStatusByStackId(stack.getId(), UPDATE_IN_PROGRESS, "Upscaling the cluster.");
flowMessageService.fireEventAndLog(stack.getId(), Msg.AMBARI_CLUSTER_SCALING_UP, UPDATE_IN_PROGRESS.name());
}
public void clusterUpscaleFinished(Stack stack, String hostgroupName) {
int numOfFailedHosts = updateMetadata(stack, hostgroupName);
boolean success = numOfFailedHosts == 0;
if (success) {
LOGGER.info("Cluster upscaled successfully");
clusterService.updateClusterStatusByStackId(stack.getId(), AVAILABLE);
flowMessageService.fireEventAndLog(stack.getId(), Msg.AMBARI_CLUSTER_SCALED_UP, AVAILABLE.name());
if (stack.getCluster().getEmailNeeded()) {
emailSenderService.sendUpscaleSuccessEmail(stack.getCluster().getOwner(), stack.getCluster().getEmailTo(),
stackUtil.extractAmbariIp(stack), stack.getCluster().getName());
flowMessageService.fireEventAndLog(stack.getId(), Msg.AMBARI_CLUSTER_NOTIFICATION_EMAIL, AVAILABLE.name());
}
} else {
LOGGER.info("Cluster upscale failed. {} hosts failed to upscale", numOfFailedHosts);
clusterService.updateClusterStatusByStackId(stack.getId(), UPDATE_FAILED);
flowMessageService.fireEventAndLog(stack.getId(), Msg.AMBARI_CLUSTER_SCALING_FAILED, UPDATE_FAILED.name(), "added to",
String.format("Ambari upscale operation failed on %d node(s).", numOfFailedHosts));
}
}
public void clusterUpscaleFailed(Stack stack, Exception errorDetails) {
LOGGER.error("Error during Cluster upscale flow: " + errorDetails.getMessage(), errorDetails);
clusterService.updateClusterStatusByStackId(stack.getId(), UPDATE_FAILED, errorDetails.getMessage());
stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.PROVISIONED,
String.format("New node(s) could not be added to the cluster: %s", errorDetails));
flowMessageService.fireEventAndLog(stack.getId(), Msg.AMBARI_CLUSTER_SCALING_FAILED, UPDATE_FAILED.name(), "added to", errorDetails);
}
private int updateMetadata(Stack stack, String hostGroupName) {
LOGGER.info("Start update metadata");
HostGroup hostGroup = hostGroupService.getByClusterIdAndName(stack.getCluster().getId(), hostGroupName);
Set<HostMetadata> hostMetadata = hostGroupService.findEmptyHostMetadataInHostGroup(hostGroup.getId());
updateFailedHostMetaData(hostMetadata);
int failedHosts = 0;
for (HostMetadata hostMeta : hostMetadata) {
if (!BYOS.equals(stack.cloudPlatform()) && hostGroup.getConstraint().getInstanceGroup() != null) {
stackService.updateMetaDataStatus(stack.getId(), hostMeta.getHostName(), InstanceStatus.REGISTERED);
}
hostGroupService.updateHostMetaDataStatus(hostMeta.getId(), HostMetadataState.HEALTHY);
if (hostMeta.getHostMetadataState() == HostMetadataState.UNHEALTHY) {
failedHosts++;
}
}
return failedHosts;
}
private void updateFailedHostMetaData(Set<HostMetadata> hostMetadata) {
List<String> upscaleHostNames = getHostNames(hostMetadata);
Set<String> successHosts = new HashSet<>(upscaleHostNames);
updateFailedHostMetaData(successHosts, hostMetadata);
}
private void updateFailedHostMetaData(Set<String> successHosts, Set<HostMetadata> hostMetadata) {
for (HostMetadata metaData : hostMetadata) {
if (!successHosts.contains(metaData.getHostName())) {
metaData.setHostMetadataState(HostMetadataState.UNHEALTHY);
hostMetadataRepository.save(metaData);
}
}
}
private List<String> getHostNames(Set<HostMetadata> hostMetadata) {
return hostMetadata.stream().map(HostMetadata::getHostName).collect(Collectors.toList());
}
}