package com.sequenceiq.cloudbreak.cloud.handler;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.sequenceiq.cloudbreak.cloud.CloudConnector;
import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext;
import com.sequenceiq.cloudbreak.cloud.context.CloudContext;
import com.sequenceiq.cloudbreak.cloud.event.instance.GetSSHFingerprintsRequest;
import com.sequenceiq.cloudbreak.cloud.event.instance.GetSSHFingerprintsResult;
import com.sequenceiq.cloudbreak.cloud.event.instance.InstanceConsoleOutputResult;
import com.sequenceiq.cloudbreak.cloud.exception.CloudOperationNotSupportedException;
import com.sequenceiq.cloudbreak.cloud.init.CloudPlatformConnectors;
import com.sequenceiq.cloudbreak.cloud.model.CloudInstance;
import com.sequenceiq.cloudbreak.cloud.scheduler.SyncPollingScheduler;
import com.sequenceiq.cloudbreak.cloud.task.PollTask;
import com.sequenceiq.cloudbreak.cloud.task.PollTaskFactory;
import reactor.bus.Event;
import reactor.bus.EventBus;
@Component
public class GetSSHFingerprintsHandler implements CloudPlatformEventHandler<GetSSHFingerprintsRequest> {
private static final Logger LOGGER = LoggerFactory.getLogger(GetSSHFingerprintsHandler.class);
@Inject
private PollTaskFactory statusCheckFactory;
@Inject
private SyncPollingScheduler<InstanceConsoleOutputResult> syncPollingScheduler;
@Inject
private CloudPlatformConnectors cloudPlatformConnectors;
@Inject
private EventBus eventBus;
@Override
public Class<GetSSHFingerprintsRequest> type() {
return GetSSHFingerprintsRequest.class;
}
@Override
public void accept(Event<GetSSHFingerprintsRequest> getSSHFingerprintsRequestEvent) {
LOGGER.info("Received event: {}", getSSHFingerprintsRequestEvent);
GetSSHFingerprintsRequest fingerprintsRequest = getSSHFingerprintsRequestEvent.getData();
try {
CloudContext cloudContext = fingerprintsRequest.getCloudContext();
CloudInstance cloudInstance = fingerprintsRequest.getCloudInstance();
CloudConnector connector = cloudPlatformConnectors.get(cloudContext.getPlatformVariant());
AuthenticatedContext ac = connector.authentication().authenticate(cloudContext, fingerprintsRequest.getCloudCredential());
GetSSHFingerprintsResult fingerprintsResult;
try {
String initialConsoleOutput = connector.instances().getConsoleOutput(ac, cloudInstance);
InstanceConsoleOutputResult consoleOutputResult = new InstanceConsoleOutputResult(cloudContext, cloudInstance, initialConsoleOutput);
PollTask<InstanceConsoleOutputResult> outputPollerTask = statusCheckFactory.newPollConsoleOutputTask(connector.instances(), ac, cloudInstance);
if (!outputPollerTask.completed(consoleOutputResult)) {
consoleOutputResult = syncPollingScheduler.schedule(outputPollerTask);
}
Set<String> sshFingerprints = FingerprintParserUtil.parseFingerprints(consoleOutputResult.getConsoleOutput());
if (sshFingerprints.isEmpty()) {
throw new RuntimeException("Failed to get SSH fingerprints from the specified VM instance.");
} else {
fingerprintsResult = new GetSSHFingerprintsResult(fingerprintsRequest, sshFingerprints);
}
} catch (CloudOperationNotSupportedException e) {
fingerprintsResult = new GetSSHFingerprintsResult(fingerprintsRequest, new HashSet<>());
}
fingerprintsRequest.getResult().onNext(fingerprintsResult);
eventBus.notify(fingerprintsResult.selector(), new Event(getSSHFingerprintsRequestEvent.getHeaders(), fingerprintsResult));
LOGGER.info("GetSSHFingerprintsHandler finished");
} catch (Exception e) {
GetSSHFingerprintsResult failure = new GetSSHFingerprintsResult("Failed to get ssh fingerprints!", e, fingerprintsRequest);
fingerprintsRequest.getResult().onNext(failure);
eventBus.notify(failure.selector(), new Event(getSSHFingerprintsRequestEvent.getHeaders(), failure));
}
}
public static class FingerprintParserUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FingerprintParserUtil.class);
private static final Pattern[] FINGERPRINT_PATTERNS = {
Pattern.compile("(?<fingerprint>([a-f0-9]{2}:){15,}[a-f0-9]{2}).*ECDSA"),
Pattern.compile("(?<fingerprint>([a-f0-9]{2}:){15,}[a-f0-9]{2}).*RSA")
};
private FingerprintParserUtil() {
}
public static Set<String> parseFingerprints(String consoleLog) {
LOGGER.debug("Received console log: {}", consoleLog);
Set<String> matchedFingerprints = new HashSet<>();
String[] lines = consoleLog.split("\n");
for (String line : lines) {
for (Pattern pattern : FINGERPRINT_PATTERNS) {
Matcher m = pattern.matcher(line);
if (m.find()) {
matchedFingerprints.add(m.group("fingerprint"));
}
}
}
return matchedFingerprints;
}
}
}