package com.sequenceiq.periscope.monitor.evaluator;
import java.util.List;
import java.util.Map;
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.periscope.domain.BaseAlert;
import com.sequenceiq.periscope.domain.Cluster;
import com.sequenceiq.periscope.domain.MetricAlert;
import com.sequenceiq.periscope.log.MDCBuilder;
import com.sequenceiq.periscope.monitor.event.ScalingEvent;
import com.sequenceiq.periscope.monitor.event.UpdateFailedEvent;
import com.sequenceiq.periscope.repository.MetricAlertRepository;
import com.sequenceiq.periscope.service.ClusterService;
import com.sequenceiq.periscope.utils.AmbariClientProvider;
import com.sequenceiq.periscope.utils.ClusterUtils;
@Component("MetricEvaluator")
@Scope("prototype")
public class MetricEvaluator extends AbstractEventPublisher implements EvaluatorExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(MetricEvaluator.class);
private static final String ALERT_STATE = "state";
private static final String ALERT_TS = "timestamp";
@Inject
private ClusterService clusterService;
@Inject
private MetricAlertRepository alertRepository;
@Inject
private AmbariClientProvider ambariClientProvider;
private long clusterId;
@Override
public void setContext(Map<String, Object> context) {
this.clusterId = (long) context.get(EvaluatorContext.CLUSTER_ID.name());
}
@Override
public void run() {
Cluster cluster = clusterService.find(clusterId);
MDCBuilder.buildMdcContext(cluster);
AmbariClient ambariClient = ambariClientProvider.createAmbariClient(cluster);
try {
for (MetricAlert alert : alertRepository.findAllByCluster(clusterId)) {
String alertName = alert.getName();
LOGGER.info("Checking metric based alert: '{}'", alertName);
List<Map<String, Object>> alertHistory = ambariClient.getAlertHistory(alert.getDefinitionName(), 1);
int historySize = alertHistory.size();
if (historySize > 1) {
LOGGER.debug("Multiple results found for alert: {}, probably HOST alert, ignoring now..", alertName);
continue;
}
if (!alertHistory.isEmpty()) {
Map<String, Object> history = alertHistory.get(0);
String currentState = (String) history.get(ALERT_STATE);
if (isAlertStateMet(currentState, alert)) {
long elapsedTime = getPeriod(history);
LOGGER.info("Alert: {} is in '{}' state since {} min(s)", alertName, currentState,
ClusterUtils.TIME_FORMAT.format((double) elapsedTime / ClusterUtils.MIN_IN_MS));
if (isPeriodReached(alert, elapsedTime) && isPolicyAttached(alert)) {
publishEvent(new ScalingEvent(alert));
break;
}
}
}
}
} catch (Exception e) {
LOGGER.error("Failed to retrieve alert history", e);
publishEvent(new UpdateFailedEvent(clusterId));
}
}
private boolean isAlertStateMet(String currentState, MetricAlert alert) {
return currentState.equalsIgnoreCase(alert.getAlertState().getValue());
}
private long getPeriod(Map<String, Object> history) {
return System.currentTimeMillis() - (long) history.get(ALERT_TS);
}
private boolean isPeriodReached(MetricAlert alert, float period) {
return period > alert.getPeriod() * ClusterUtils.MIN_IN_MS;
}
private boolean isPolicyAttached(BaseAlert alert) {
return alert.getScalingPolicy() != null;
}
}