package com.sequenceiq.it.cloudbreak;
import static java.lang.Boolean.FALSE;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.testng.Assert;
import com.sequenceiq.ambari.client.AmbariClient;
import com.sequenceiq.cloudbreak.api.endpoint.EventEndpoint;
import com.sequenceiq.cloudbreak.api.endpoint.StackEndpoint;
import com.sequenceiq.cloudbreak.api.model.CloudbreakEventsJson;
import com.sequenceiq.cloudbreak.api.model.HostGroupResponse;
import com.sequenceiq.cloudbreak.api.model.HostMetadataResponse;
import com.sequenceiq.cloudbreak.api.model.InstanceGroupResponse;
import com.sequenceiq.cloudbreak.api.model.StackResponse;
import com.sequenceiq.cloudbreak.api.model.Status;
import com.sequenceiq.cloudbreak.client.CloudbreakClient;
import com.sequenceiq.it.IntegrationTestContext;
import com.sequenceiq.periscope.api.endpoint.HistoryEndpoint;
import com.sequenceiq.periscope.api.model.HistoryJson;
import com.sequenceiq.periscope.client.AutoscaleClient;
import groovyx.net.http.HttpResponseException;
public class CloudbreakUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(CloudbreakUtil.class);
private static final int MAX_RETRY = 360;
private static final int POLLING_INTERVAL = 10000;
private CloudbreakUtil() {
}
public static void checkResponse(String operation, Response response) {
if (Response.Status.Family.SUCCESSFUL != response.getStatusInfo().getFamily()) {
String errormsg = "Error happened during " + operation + " rest operation: status: " + response.getStatus() + ", error: "
+ response.readEntity(String.class);
LOGGER.error(errormsg);
throw new RuntimeException(errormsg);
}
}
public static void waitAndCheckStackStatus(CloudbreakClient cloudbreakClient, String stackId, String desiredStatus) {
waitAndCheckStatuses(cloudbreakClient, stackId, Collections.singletonMap("status", desiredStatus));
}
public static void waitAndCheckClusterStatus(CloudbreakClient cloudbreakClient, String stackId, String desiredStatus) {
waitAndCheckStatuses(cloudbreakClient, stackId, Collections.singletonMap("clusterStatus", desiredStatus));
}
public static WaitResult waitForStackStatus(CloudbreakClient cloudbreakClient, String stackId, String desiredStatus) {
return waitForStatuses(cloudbreakClient, stackId, Collections.singletonMap("status", desiredStatus));
}
public static WaitResult waitForClusterStatus(CloudbreakClient cloudbreakClient, String stackId, String desiredStatus) {
return waitForStatuses(cloudbreakClient, stackId, Collections.singletonMap("clusterStatus", desiredStatus));
}
public static void waitAndCheckStatuses(CloudbreakClient cloudbreakClient, String stackId, Map<String, String> desiredStatuses) {
for (int i = 0; i < 3; i++) {
WaitResult waitResult = waitForStatuses(cloudbreakClient, stackId, desiredStatuses);
if (waitResult == WaitResult.FAILED) {
Assert.fail("The stack has failed");
}
if (waitResult == WaitResult.TIMEOUT) {
Assert.fail("Timeout happened");
}
}
}
public static WaitResult waitForHostStatusStack(StackEndpoint stackEndpoint, String stackId, String hostGroup, String desiredStatus) {
WaitResult waitResult = WaitResult.SUCCESSFUL;
Boolean found = FALSE;
int retryCount = 0;
do {
LOGGER.info("Waiting for host status {} in hostgroup {} ...", desiredStatus, hostGroup);
sleep();
StackResponse stackResponse = stackEndpoint.get(Long.valueOf(stackId));
Set<HostGroupResponse> hostGroupResponse = stackResponse.getCluster().getHostGroups();
for (HostGroupResponse hr : hostGroupResponse) {
if (hr.getName().equals(hostGroup)) {
Set<HostMetadataResponse> hostMetadataResponses = hr.getMetadata();
for (HostMetadataResponse hmr : hostMetadataResponses) {
if (hmr.getState().equals(desiredStatus)) {
found = Boolean.TRUE;
}
}
}
}
retryCount++;
} while (!found && (retryCount < MAX_RETRY));
if (retryCount == MAX_RETRY) {
waitResult = WaitResult.TIMEOUT;
}
return waitResult;
}
public static void checkClusterFailed(StackEndpoint stackEndpoint, String stackId, String failMessage) {
StackResponse stackResponse = stackEndpoint.get(Long.valueOf(stackId));
Assert.assertEquals(stackResponse.getCluster().getStatus(), "CREATE_FAILED");
Assert.assertTrue(stackResponse.getCluster().getStatusReason().contains(failMessage));
}
public static void checkClusterAvailability(StackEndpoint stackEndpoint, String port, String stackId, String ambariUser, String ambariPassowrd,
boolean checkAmbari) {
StackResponse stackResponse = stackEndpoint.get(Long.valueOf(stackId));
Assert.assertEquals(stackResponse.getCluster().getStatus(), "AVAILABLE", "The cluster hasn't been started!");
Assert.assertEquals(stackResponse.getStatus(), Status.AVAILABLE, "The stack hasn't been started!");
String ambariIp = stackResponse.getCluster().getAmbariServerIp();
Assert.assertNotNull(ambariIp, "The Ambari IP is not available!");
if (checkAmbari) {
AmbariClient ambariClient = new AmbariClient(ambariIp, port, ambariUser, ambariPassowrd);
Assert.assertEquals(ambariClient.healthCheck(), "RUNNING", "The Ambari server is not running!");
Assert.assertEquals(ambariClient.getClusterHosts().size(), getNodeCount(stackResponse),
"The number of cluster nodes in the stack differs from the number of nodes registered in ambari");
}
}
public static void checkClusterStopped(StackEndpoint stackEndpoint, String port, String stackId, String ambariUser, String ambariPassword) {
StackResponse stackResponse = stackEndpoint.get(Long.valueOf(stackId));
Assert.assertEquals(stackResponse.getCluster().getStatus(), "STOPPED", "The cluster is not stopped!");
Assert.assertEquals(stackResponse.getStatus(), Status.STOPPED, "The stack is not stopped!");
String ambariIp = stackResponse.getCluster().getAmbariServerIp();
AmbariClient ambariClient = new AmbariClient(ambariIp, port, ambariUser, ambariPassword);
Assert.assertFalse(isAmbariRunning(ambariClient), "The Ambari server is running in stopped state!");
}
public static boolean isAmbariRunning(AmbariClient ambariClient) {
try {
String ambariHealth = ambariClient.healthCheck();
return "RUNNING".equals(ambariHealth);
} catch (Exception e) {
return false;
}
}
public static String getAmbariIp(StackEndpoint stackEndpoint, String stackId, IntegrationTestContext itContext) {
String ambariIp = itContext.getContextParam(CloudbreakITContextConstants.AMBARI_IP_ID);
if (StringUtils.isEmpty(ambariIp)) {
StackResponse stackResponse = stackEndpoint.get(Long.valueOf(stackId));
ambariIp = stackResponse.getCluster().getAmbariServerIp();
Assert.assertNotNull(ambariIp, "The Ambari IP is not available!");
itContext.putContextParam(CloudbreakITContextConstants.AMBARI_IP_ID, ambariIp);
}
return ambariIp;
}
private static WaitResult waitForStatuses(CloudbreakClient cloudbreakClient, String stackId, Map<String, String> desiredStatuses) {
WaitResult waitResult = WaitResult.SUCCESSFUL;
Map<String, String> currentStatuses = new HashMap<>();
int retryCount = 0;
do {
LOGGER.info("Waiting for status(es) {}, stack id: {}, current status(es) {} ...", desiredStatuses, stackId, currentStatuses);
sleep();
StackEndpoint stackEndpoint = cloudbreakClient.stackEndpoint();
try {
Map<String, Object> statusResult = stackEndpoint.status(Long.valueOf(stackId));
for (String statusPath : desiredStatuses.keySet()) {
currentStatuses.put(statusPath, (String) statusResult.get(statusPath));
}
} catch (Exception exception) {
if (exception instanceof HttpResponseException && ((HttpResponseException) exception).getStatusCode() == HttpStatus.NOT_FOUND.value()) {
for (String statusPath : desiredStatuses.keySet()) {
currentStatuses.put(statusPath, "DELETE_COMPLETED");
}
} else {
continue;
}
}
retryCount++;
} while (!checkStatuses(currentStatuses, desiredStatuses) && !checkFailedStatuses(currentStatuses) && retryCount < MAX_RETRY);
LOGGER.info("Status(es) {} for {} are in desired status(es) {}", desiredStatuses.keySet(), stackId, currentStatuses.values());
if (currentStatuses.containsValue("FAILED") || checkNotExpectedDelete(currentStatuses, desiredStatuses)) {
waitResult = WaitResult.FAILED;
}
if (retryCount == MAX_RETRY) {
waitResult = WaitResult.TIMEOUT;
}
return waitResult;
}
public static WaitResult waitForEvent(CloudbreakClient cloudbreakClient, String stackName, String eventType, String eventMessage, long sinceTimeStamp) {
WaitResult waitResult = WaitResult.SUCCESSFUL;
Boolean exitCriteria = FALSE;
int retryCount = 0;
do {
LOGGER.info("Waiting for event type {} and event message contains {} ...", eventType, eventMessage);
sleep();
EventEndpoint eventEndpoint = cloudbreakClient.eventEndpoint();
List<CloudbreakEventsJson> list = eventEndpoint.get(sinceTimeStamp);
for (CloudbreakEventsJson event : list) {
if (event.getStackName().equals(stackName) && event.getEventMessage().contains(eventMessage) && event.getEventType().equals(eventType)) {
exitCriteria = Boolean.TRUE;
break;
}
}
retryCount++;
} while (!exitCriteria && retryCount < MAX_RETRY);
LOGGER.info("Event {} for {} happened and event message contains {}", eventType, stackName, eventMessage);
if (retryCount == MAX_RETRY) {
waitResult = WaitResult.TIMEOUT;
}
return waitResult;
}
public static WaitResult waitForAutoScalingEvent(AutoscaleClient autoscaleClient, Long clusterId, Long currentTime) {
WaitResult waitResult = WaitResult.SUCCESSFUL;
Boolean exitCriteria = FALSE;
int retryCount = 0;
do {
LOGGER.info("Waiting for auto scaling event is success ...");
sleep();
HistoryEndpoint historyEndpoint = autoscaleClient.historyEndpoint();
List<HistoryJson> historyJson = historyEndpoint.getHistory(clusterId);
for (HistoryJson elem : historyJson) {
if ((elem.getTimestamp() > currentTime) && elem.getScalingStatus().toString().equals("SUCCESS")) {
exitCriteria = Boolean.TRUE;
}
}
retryCount++;
} while (!exitCriteria && retryCount < MAX_RETRY);
LOGGER.info("Auto scaling event happened successfully");
if (retryCount == MAX_RETRY) {
waitResult = WaitResult.TIMEOUT;
}
return waitResult;
}
private static boolean checkStatuses(Map<String, String> currentStatuses, Map<String, String> desiredStatuses) {
boolean result = true;
for (Map.Entry<String, String> desiredStatus: desiredStatuses.entrySet()) {
if (!desiredStatus.getValue().equals(currentStatuses.get(desiredStatus.getKey()))) {
result = false;
break;
}
}
return result;
}
private static boolean checkFailedStatuses(Map<String, String> currentStatuses) {
boolean result = false;
List<String> failedStatuses = Arrays.asList("FAILED", "DELETE_COMPLETED");
for (Map.Entry<String, String> desiredStatus: currentStatuses.entrySet()) {
if (failedStatuses.contains(desiredStatus.getValue())) {
result = true;
break;
}
}
return result;
}
private static boolean checkNotExpectedDelete(Map<String, String> currentStatuses, Map<String, String> desiredStatuses) {
boolean result = false;
for (Map.Entry<String, String> desiredStatus: desiredStatuses.entrySet()) {
if (!desiredStatus.getValue().equals("DELETE_COMPLETED") && currentStatuses.get(desiredStatus.getKey()).equals("DELETE_COMPLETED")) {
result = true;
break;
}
}
return result;
}
public static void sleep() {
try {
Thread.sleep(POLLING_INTERVAL);
} catch (InterruptedException e) {
LOGGER.warn("Ex during wait: {}", e);
}
}
private static int getNodeCount(StackResponse stackResponse) {
List<InstanceGroupResponse> instanceGroups = stackResponse.getInstanceGroups();
int nodeCount = 0;
for (InstanceGroupResponse instanceGroup : instanceGroups) {
if (!instanceGroup.getGroup().equals("cbgateway")) {
nodeCount += instanceGroup.getNodeCount();
}
}
return nodeCount;
}
}