package com.sequenceiq.cloudbreak.orchestrator.salt.poller;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import com.sequenceiq.cloudbreak.orchestrator.OrchestratorBootstrap;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorFailedException;
import com.sequenceiq.cloudbreak.orchestrator.model.GatewayConfig;
import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponse;
import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponses;
import com.sequenceiq.cloudbreak.orchestrator.model.Node;
import com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltActionType;
import com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltConnector;
import com.sequenceiq.cloudbreak.orchestrator.salt.client.target.Glob;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.Minion;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.SaltAction;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.SaltAuth;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.SaltMaster;
import com.sequenceiq.cloudbreak.orchestrator.salt.states.SaltStates;
public class SaltBootstrap implements OrchestratorBootstrap {
private static final Logger LOGGER = LoggerFactory.getLogger(SaltBootstrap.class);
private final SaltConnector sc;
private final List<GatewayConfig> allGatewayConfigs;
private final Set<Node> originalTargets;
private Set<Node> targets;
private String domain;
public SaltBootstrap(SaltConnector sc, List<GatewayConfig> allGatewayConfigs, Set<Node> targets, String domain) {
this.sc = sc;
this.allGatewayConfigs = allGatewayConfigs;
this.originalTargets = Collections.unmodifiableSet(targets);
this.targets = targets;
this.domain = domain;
}
@Override
public Boolean call() throws Exception {
LOGGER.info("Bootstrapping of nodes [{}/{}]", originalTargets.size() - targets.size(), originalTargets.size());
if (!targets.isEmpty()) {
LOGGER.info("Missing targets for SaltBootstrap: {}", targets);
SaltAction saltAction = createBootstrap();
GenericResponses responses = sc.action(saltAction);
Set<Node> failedTargets = new HashSet<>();
LOGGER.info("SaltBootstrap responses: {}", responses);
for (GenericResponse genericResponse : responses.getResponses()) {
if (genericResponse.getStatusCode() != HttpStatus.OK.value()) {
LOGGER.info("Failed to distributed salt run to: " + genericResponse.getAddress());
String address = genericResponse.getAddress().split(":")[0];
failedTargets.addAll(originalTargets.stream().filter(a -> a.getPrivateIp().equals(address)).collect(Collectors.toList()));
}
}
targets = failedTargets;
if (!targets.isEmpty()) {
LOGGER.info("Missing nodes to run saltbootstrap: {}", targets);
throw new CloudbreakOrchestratorFailedException("There are missing nodes from saltbootstrap: " + targets);
}
}
Map<String, String> networkResult = SaltStates.networkInterfaceIP(sc, Glob.ALL).getResultGroupByIP();
originalTargets.forEach(node -> {
if (!networkResult.containsKey(node.getPrivateIp())) {
LOGGER.info("Salt-minion is not responding on host: {}, yet", node);
targets.add(node);
}
});
if (!targets.isEmpty()) {
throw new CloudbreakOrchestratorFailedException("There are missing nodes from salt network response: " + targets);
}
LOGGER.info("Bootstrapping of nodes completed: {}", originalTargets.size());
return true;
}
private SaltAction createBootstrap() {
SaltAction saltAction = new SaltAction(SaltActionType.RUN);
SaltAuth auth = new SaltAuth();
auth.setPassword(sc.getSaltPassword());
List<String> targetIps = targets.stream().map(Node::getPrivateIp).collect(Collectors.toList());
for (GatewayConfig gatewayConfig : allGatewayConfigs) {
String gatewayAddress = gatewayConfig.getPrivateAddress();
if (targetIps.contains(gatewayAddress)) {
SaltMaster master = new SaltMaster();
master.setAddress(gatewayAddress);
master.setAuth(auth);
master.setDomain(domain);
saltAction.addMaster(master);
// set due to compatibility reasons
saltAction.setMaster(master);
saltAction.setServer(gatewayAddress);
Node saltMaster = targets.stream().filter(n -> n.getPrivateIp().equals(gatewayAddress)).findFirst().get();
saltAction.addMinion(createMinion(saltMaster));
}
}
for (Node minion : targets.stream().filter(node -> !getGatewayPrivateIps().contains(node.getPrivateIp())).collect(Collectors.toList())) {
saltAction.addMinion(createMinion(minion));
}
return saltAction;
}
private Minion createMinion(Node node) {
Minion minion = new Minion();
minion.setAddress(node.getPrivateIp());
minion.setRoles(Collections.emptyList());
minion.setHostGroup(node.getHostGroup());
minion.setDomain(domain);
minion.setServers(getGatewayPrivateIps());
// set due to compatibility reasons
minion.setServer(getGatewayPrivateIps().get(0));
return minion;
}
private List<String> getGatewayPrivateIps() {
return allGatewayConfigs.stream().map(GatewayConfig::getPrivateAddress).collect(Collectors.toList());
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("SaltBootstrap{");
sb.append("sc=").append(sc);
sb.append(", allGatewayConfigs=").append(allGatewayConfigs);
sb.append(", originalTargets=").append(originalTargets);
sb.append(", targets=").append(targets);
sb.append(", domain='").append(domain).append('\'');
sb.append('}');
return sb.toString();
}
}