package com.sequenceiq.periscope.monitor.evaluator; import static com.sequenceiq.periscope.api.model.ClusterState.PENDING; import static com.sequenceiq.periscope.api.model.ClusterState.RUNNING; import static com.sequenceiq.periscope.api.model.ClusterState.SUSPENDED; import java.util.Optional; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.sequenceiq.ambari.client.AmbariClient; import com.sequenceiq.cloudbreak.api.model.AmbariAddressJson; import com.sequenceiq.cloudbreak.api.model.AutoscaleStackResponse; import com.sequenceiq.periscope.api.model.ScalingStatus; import com.sequenceiq.periscope.domain.Ambari; import com.sequenceiq.periscope.domain.Cluster; import com.sequenceiq.periscope.domain.History; import com.sequenceiq.periscope.domain.PeriscopeUser; import com.sequenceiq.periscope.domain.SecurityConfig; import com.sequenceiq.periscope.log.MDCBuilder; import com.sequenceiq.periscope.model.AmbariStack; import com.sequenceiq.periscope.model.ClusterCreationEvaluatorContext; import com.sequenceiq.periscope.notification.HttpNotificationSender; import com.sequenceiq.periscope.service.ClusterService; import com.sequenceiq.periscope.service.HistoryService; import com.sequenceiq.periscope.service.security.TlsConfigurationException; import com.sequenceiq.periscope.service.security.TlsSecurityService; import com.sequenceiq.periscope.utils.AmbariClientProvider; @Component("ClusterCreationEvaluator") @Scope("prototype") public class ClusterCreationEvaluator implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(ClusterCreationEvaluator.class); @Inject private ClusterService clusterService; @Inject private AmbariClientProvider ambariClientProvider; @Inject private TlsSecurityService tlsSecurityService; @Inject private HistoryService historyService; @Inject private HttpNotificationSender notificationSender; private ClusterCreationEvaluatorContext context; @Override public void run() { AutoscaleStackResponse stack = context.getStack(); Optional<Cluster> clusterOptional = context.getClusterOptional(); try { createOrUpdateCluster(stack, clusterOptional); } catch (AmbariHealtCheckFailed ahf) { LOGGER.warn(String.format("Ambari health check failed for Cloudbreak stack: %s(ID:%s)", stack.getStackId(), stack.getName()), ahf); } catch (TlsConfigurationException ex) { LOGGER.warn(String.format("Could not prepare TLS configuration for Cloudbreak stack: %s(ID:%s)", stack.getStackId(), stack.getName()), ex); } catch (Exception ex) { LOGGER.warn(String.format("Could not create cluster for Cloudbreak stack: %s(ID:%s)", stack.getStackId(), stack.getName()), ex); } } public void setContext(ClusterCreationEvaluatorContext context) { this.context = context; } private void createOrUpdateCluster(AutoscaleStackResponse stack, Optional<Cluster> clusterOptional) { AmbariStack resolvedAmbari = createAmbariStack(stack); Cluster cluster; boolean sendNotification = false; if (clusterOptional.isPresent()) { cluster = clusterOptional.get(); MDCBuilder.buildMdcContext(cluster); if (PENDING.equals(cluster.getState()) || SUSPENDED.equals(cluster.getState())) { ambariHealthCheck(cluster.getUser(), resolvedAmbari); LOGGER.info("Update cluster and set it's state to 'RUNNING' for Ambari host: {}", resolvedAmbari.getAmbari().getHost()); cluster = clusterService.update(cluster.getId(), resolvedAmbari, false, RUNNING); sendNotification = true; } } else { LOGGER.info("Creating cluster for Ambari host: {}", resolvedAmbari.getAmbari().getHost()); PeriscopeUser user = new PeriscopeUser(stack.getOwner(), null, stack.getAccount()); ambariHealthCheck(user, resolvedAmbari); cluster = clusterService.create(user, resolvedAmbari, null); sendNotification = true; } if (sendNotification) { History history = historyService.createEntry(ScalingStatus.ENABLED, "Autoscaling has been enabled for the cluster.", 0, cluster); notificationSender.send(history); } } private AmbariStack createAmbariStack(AutoscaleStackResponse stack) { String host = stack.getAmbariServerIp(); AmbariAddressJson ambariAddressJson = new AmbariAddressJson(); ambariAddressJson.setAmbariAddress(host); String gatewayPort = String.valueOf(stack.getGatewayPort()); SecurityConfig securityConfig = tlsSecurityService.prepareSecurityConfig(stack.getStackId()); return new AmbariStack(new Ambari(host, gatewayPort, stack.getUserName(), stack.getPassword()), stack.getStackId(), securityConfig); } private void ambariHealthCheck(PeriscopeUser user, AmbariStack ambariStack) { String host = ambariStack.getAmbari().getHost(); try { AmbariClient client = ambariClientProvider.createAmbariClient(new Cluster(user, ambariStack)); String healthCheckResult = client.healthCheck(); if (!"RUNNING".equals(healthCheckResult)) { throw new AmbariHealtCheckFailed(String.format("Ambari on host '%s' is not in 'RUNNING' state.", host)); } } catch (Exception ex) { throw new AmbariHealtCheckFailed(String.format("Health check failed on host '%s':", host), ex); } } private class AmbariHealtCheckFailed extends RuntimeException { AmbariHealtCheckFailed(String message) { super(message); } AmbariHealtCheckFailed(String message, Throwable cause) { super(message, cause); } } }