package com.sequenceiq.cloudbreak.orchestrator.salt.states;
import static com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltClientType.LOCAL;
import static com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltClientType.LOCAL_ASYNC;
import static com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltClientType.RUNNER;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
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.client.target.Target;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.ApplyResponse;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.Minion;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.NetworkInterfaceResponse;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.PingResponse;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.RunnerInfo;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.RunningJobsResponse;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.SaltAction;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.StateType;
import com.sequenceiq.cloudbreak.util.JsonUtil;
public class SaltStates {
private static final Logger LOGGER = LoggerFactory.getLogger(SaltStates.class);
private SaltStates() {
}
public static String ambariReset(SaltConnector sc, Target<String> target) {
return applyState(sc, "ambari.reset", target).getJid();
}
public static ApplyResponse addGrain(SaltConnector sc, Target<String> target, String key, String value) {
return sc.run(target, "grains.append", LOCAL, ApplyResponse.class, key, value);
}
public static ApplyResponse removeGrain(SaltConnector sc, Target<String> target, String key, String value) {
return sc.run(target, "grains.remove", LOCAL, ApplyResponse.class, key, value);
}
public static ApplyResponse syncGrains(SaltConnector sc) {
return sc.run(Glob.ALL, "saltutil.sync_grains", LOCAL, ApplyResponse.class);
}
public static ApplyResponse updateMine(SaltConnector sc) {
return sc.run(Glob.ALL, "mine.update", LOCAL, ApplyResponse.class);
}
public static String highstate(SaltConnector sc) {
return sc.run(Glob.ALL, "state.highstate", LOCAL_ASYNC, ApplyResponse.class).getJid();
}
public static Multimap<String, String> jidInfo(SaltConnector sc, String jid, Target<String> target, StateType stateType) {
if (StateType.HIGH.equals(stateType)) {
return highStateJidInfo(sc, jid);
} else if (StateType.SIMPLE.equals(stateType)) {
return applyStateJidInfo(sc, jid);
}
return ArrayListMultimap.create();
}
private static Multimap<String, String> applyStateJidInfo(SaltConnector sc, String jid) {
Map jidInfo = sc.run("jobs.lookup_jid", RUNNER, Map.class, "jid", jid);
LOGGER.info("Salt apply state jid info: {}", jidInfo);
Map<String, List<RunnerInfo>> states = JidInfoResponseTransformer.getSimpleStates(jidInfo);
return collectMissingTargets(states);
}
private static Multimap<String, String> highStateJidInfo(SaltConnector sc, String jid) {
Map jidInfo = sc.run("jobs.lookup_jid", RUNNER, Map.class, "jid", jid);
LOGGER.info("Salt high state jid info: {}", jidInfo);
Map<String, List<RunnerInfo>> states = JidInfoResponseTransformer.getHighStates(jidInfo);
return collectMissingTargets(states);
}
private static Multimap<String, String> collectMissingTargets(Map<String, List<RunnerInfo>> stringRunnerInfoObjectMap) {
Multimap<String, String> missingTargetsWithErrors = ArrayListMultimap.create();
for (Map.Entry<String, List<RunnerInfo>> stringMapEntry : stringRunnerInfoObjectMap.entrySet()) {
LOGGER.info("Collect missing targets from host: {}", stringMapEntry.getKey());
logRunnerInfos(stringMapEntry);
for (RunnerInfo targetObject : stringMapEntry.getValue()) {
if (targetObject.getResult()) {
LOGGER.info("{} finished in {} ms.", targetObject.getComment(), targetObject.getDuration());
} else {
LOGGER.info("{} job state is {}.", targetObject.getComment(), targetObject.getResult());
missingTargetsWithErrors.put(stringMapEntry.getKey(), targetObject.getComment());
}
}
}
return missingTargetsWithErrors;
}
private static void logRunnerInfos(Map.Entry<String, List<RunnerInfo>> stringMapEntry) {
List<RunnerInfo> runnerInfos = stringMapEntry.getValue();
Collections.sort(runnerInfos, Collections.reverseOrder(new RunnerInfo.DurationComparator()));
double sum = runnerInfos.stream().mapToDouble(runnerInfo -> runnerInfo.getDuration()).sum();
try {
LOGGER.info("Salt states executed on: {} within: {} sec, details {}", stringMapEntry.getKey(),
TimeUnit.MILLISECONDS.toSeconds(Math.round(sum)), JsonUtil.writeValueAsString(runnerInfos));
} catch (JsonProcessingException e) {
LOGGER.warn("Failed to serialise runnerInfos. Salt states executed on: {} within: {} sec", stringMapEntry.getKey(),
TimeUnit.MILLISECONDS.toSeconds(Math.round(sum)));
}
}
public static boolean jobIsRunning(SaltConnector sc, String jid) {
RunningJobsResponse runningInfo = sc.run("jobs.active", RUNNER, RunningJobsResponse.class);
LOGGER.info("Active salt jobs: {}", runningInfo);
for (Map<String, Map<String, Object>> results : runningInfo.getResult()) {
for (Map.Entry<String, Map<String, Object>> stringMapEntry : results.entrySet()) {
if (stringMapEntry.getKey().equals(jid)) {
return true;
}
}
}
return false;
}
public static PingResponse ping(SaltConnector sc, Target<String> target) {
return sc.run(target, "test.ping", LOCAL, PingResponse.class);
}
public static NetworkInterfaceResponse networkInterfaceIP(SaltConnector sc, Target<String> target) {
return sc.run(target, "network.interface_ip", LOCAL, NetworkInterfaceResponse.class, "eth0");
}
public static void stopMinions(SaltConnector sc, Map<String, String> privateIPsByFQDN) {
// This is slow
// String targetIps = "S@" + hostnames.stream().collect(Collectors.joining(" or S@"));
//Map<String, String> ipToMinionId = SaltStates.networkInterfaceIP(sc, new Compound(targetIps)).getResultGroupByHost();
SaltAction saltAction = new SaltAction(SaltActionType.STOP);
for (Map.Entry<String, String> entry : privateIPsByFQDN.entrySet()) {
Minion minion = new Minion();
minion.setAddress(entry.getValue());
saltAction.addMinion(minion);
}
sc.action(saltAction);
}
private static ApplyResponse applyState(SaltConnector sc, String service, Target<String> target) {
return sc.run(target, "state.apply", LOCAL_ASYNC, ApplyResponse.class, service);
}
}