package org.rhq.enterprise.server.rest.reporting;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.alert.AlertCondition;
import org.rhq.core.domain.alert.AlertConditionCategory;
import org.rhq.core.domain.alert.AlertConditionLog;
import org.rhq.core.domain.alert.AlertConditionOperator;
import org.rhq.core.domain.alert.AlertPriority;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
import org.rhq.enterprise.server.alert.AlertManagerLocal;
import org.rhq.enterprise.server.rest.AbstractRestBean;
import org.rhq.enterprise.server.rest.ReportsInterceptor;
import org.rhq.enterprise.server.util.CriteriaQuery;
import org.rhq.enterprise.server.util.CriteriaQueryExecutor;
@Interceptors(ReportsInterceptor.class)
@Stateless
public class RecentAlertHandler extends AbstractRestBean implements RecentAlertLocal {
private final Log log = LogFactory.getLog(RecentAlertHandler.class);
@EJB
private AlertManagerLocal alertManager;
public StreamingOutput recentAlertsInternal(
String alertPriority,
Long startTime,
Long endTime,
HttpServletRequest request,
Subject user
) {
this.caller = user;
return recentAlerts(alertPriority,startTime,endTime,request);
}
@Override
public StreamingOutput recentAlerts(final String alertPriority, final Long startTime, final Long endTime,
final HttpServletRequest request) {
if (log.isDebugEnabled()) {
log.debug("Received request to generate report for " + caller);
}
return new StreamingOutput() {
@Override
public void write(OutputStream stream) throws IOException, WebApplicationException {
final AlertCriteria criteria = new AlertCriteria();
criteria.addSortCtime(PageOrdering.DESC);
if(startTime != null){
criteria.addFilterStartTime(startTime);
}
if(endTime != null){
criteria.addFilterEndTime(endTime);
}
// lets default the end time for them to now if they didnt enter it
if(startTime != null && endTime == null){
Date today = new Date();
criteria.addFilterEndTime(today.getTime());
}
criteria.addFilterPriorities(getAlertPriorities());
CriteriaQueryExecutor<Alert, AlertCriteria> queryExecutor =
new CriteriaQueryExecutor<Alert, AlertCriteria>() {
@Override
public PageList<Alert> execute(AlertCriteria criteria) {
return alertManager.findAlertsByCriteria(caller, criteria);
}
};
CriteriaQuery<Alert, AlertCriteria> query =
new CriteriaQuery<Alert, AlertCriteria>(criteria, queryExecutor);
CsvWriter<Alert> csvWriter = new CsvWriter<Alert>();
csvWriter.setColumns("ctime", "alertDefinition.name", "conditionText", "alertDefinition.priority",
"status", "alertDefinition.resource.name", "ancestry", "detailsURL");
csvWriter.setPropertyConverter("ctime", csvWriter.DATE_CONVERTER);
csvWriter.setPropertyConverter("conditionText", new PropertyConverter<Alert>() {
@Override
public Object convert(Alert alert, String propertyName) {
return getConditionText(alert);
}
});
csvWriter.setPropertyConverter("status", new PropertyConverter<Alert>() {
@Override
public Object convert(Alert alert, String propertyName) {
return getStatus(alert);
}
});
csvWriter.setPropertyConverter("ancestry", new PropertyConverter<Alert>() {
@Override
public Object convert(Alert alert, String propertyName) {
return ReportFormatHelper.parseAncestry(alert.getAlertDefinition().getResource().getAncestry());
}
});
csvWriter.setPropertyConverter("detailsURL", new PropertyConverter<Alert>() {
@Override
public Object convert(Alert alert, String propertyName) {
return getDetailsURL(alert);
}
});
stream.write((getHeader() + "\n").getBytes());
for (Alert alert : query) {
csvWriter.write(alert, stream);
}
}
private AlertPriority[] getAlertPriorities() {
List<AlertPriority> alertPriorityList = new ArrayList<AlertPriority>(10);
String alertPriorities[] = alertPriority.split(",");
for ( String alertPriorityValue : alertPriorities) {
log.info("Alert Priority Filter set for: " + alertPriorityValue);
alertPriorityList.add(AlertPriority.valueOf(alertPriorityValue.toUpperCase()));
}
return alertPriorityList.toArray(new AlertPriority[alertPriorityList.size()]);
}
private String getHeader(){
return "Creation Time,Name,Condition Text,Priority,Status,Resource,Ancestry,Details URL";
}
private String getStatus(Alert alert) {
if (alert.getAcknowledgeTime() == null || alert.getAcknowledgeTime() < 0) {
return "No Ack";
}
return "Ack (" + alert.getAcknowledgingSubject() + ")";
}
private String getDetailsURL(Alert alert) {
String protocol;
if (request.isSecure()) {
protocol = "https";
} else {
protocol = "http";
}
return protocol + "://" + request.getServerName() + ":" + request.getServerPort() +
"/coregui/#Resource/" + alert.getAlertDefinition().getResource().getId() + "/Alerts/History/" +
alert.getId();
}
private String getConditionText(Alert alert) {
Set<AlertConditionLog> conditionLogs = alert.getConditionLogs();
String conditionText = null;
String conditionValue;
if (conditionLogs.size() > 1) {
conditionText = "Multiple Conditions";
} else if (conditionLogs.size() == 1) {
AlertConditionLog conditionLog = conditionLogs.iterator().next();
AlertCondition condition = conditionLog.getCondition();
conditionText = formatCondition(condition);
conditionValue = conditionLog.getValue();
if (condition.getMeasurementDefinition() != null) {
try {
conditionValue = MeasurementConverter.format(conditionLog.getValue(),
condition.getMeasurementDefinition().getUnits());
} catch (Exception e) {
// the condition log value was probably not a number (most likely a trait). Ignore this exception.
// even if any other errors occur trying to format the value, ignore this and just use the raw value string
}
}
} else {
conditionText = "No Conditions";
conditionValue = "--";
}
return conditionText;
}
private String formatCondition(AlertCondition condition) {
StringBuilder builder = new StringBuilder();
AlertConditionCategory category = condition.getCategory();
AlertConditionOperator operator;
String formattedThreshold;
switch (category) {
case AVAILABILITY:
builder.append("Availability [");
operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase());
switch (operator) {
case AVAIL_GOES_DISABLED:
builder.append("Goes disabled");
break;
case AVAIL_GOES_DOWN:
builder.append("Goes down");
break;
case AVAIL_GOES_UNKNOWN:
builder.append("Goes unknown");
break;
case AVAIL_GOES_UP:
builder.append("Goes up");
break;
case AVAIL_GOES_NOT_UP:
builder.append("Goes not up");
break;
default:
builder.append("*ERROR*");
}
builder.append("]");
break;
case AVAIL_DURATION:
builder.append("Availability Duration [");
operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase());
switch (operator) {
case AVAIL_DURATION_DOWN:
builder.append("Stays Down");
break;
case AVAIL_DURATION_NOT_UP:
builder.append("Stays Not Up");
break;
default:
builder.append("*ERROR*");
}
builder.append(" For ");
String value = String.valueOf(Integer.valueOf(condition.getOption()) / 60);
String formatted = MeasurementConverter.format(value, MeasurementUnits.MINUTES);
builder.append(formatted).append("]");
break;
case THRESHOLD:
MeasurementUnits units = condition.getMeasurementDefinition().getUnits();
formattedThreshold = MeasurementConverter.format(condition.getThreshold(), units, true);
if (condition.getOption() == null) {
builder.append("Metric Value Threshold [")
.append(condition.getName())
.append(" ")
.append(condition.getComparator())
.append(" ")
.append(formattedThreshold)
.append("]");
} else {
// this is a calltime threshold condition
builder.append("Call Time Value Threshold [");
if (condition.getMeasurementDefinition() != null) {
builder.append(condition.getMeasurementDefinition().getDisplayName()).append(" ");
}
builder.append(condition.getOption()) // MIN, MAX, AVG (never null)
.append(" ")
.append(condition.getComparator()) // <, >, =
.append(" ")
.append(formattedThreshold)
.append("]");
if (condition.getName() != null && condition.getName().length() > 0) {
builder.append(" with call destination matching '")
.append(condition.getName())
.append("'");
}
}
break;
case BASELINE:
formattedThreshold = MeasurementConverter.format(condition.getThreshold(),
MeasurementUnits.PERCENTAGE, true);
builder.append("Metric Value Baseline [")
.append(condition.getName())
.append(" ")
.append(condition.getComparator())
.append(" ")
.append(formattedThreshold)
.append(" of ")
.append(condition.getOption())
.append("]");
break;
case CHANGE:
if (condition.getOption() == null) {
builder.append("Metric Value Change [")
.append(condition.getName())
.append(" ")
.append("]");
} else {
// this is a calltime change condition
formattedThreshold = MeasurementConverter.format(condition.getThreshold(),
MeasurementUnits.PERCENTAGE, true);
builder.append("Call Time Value Changes [");
if (condition.getMeasurementDefinition() != null) {
builder.append(condition.getMeasurementDefinition().getDisplayName()).append(" ");
}
builder.append(condition.getOption()) // MIN, MAX, AVG (never null)
.append(" ")
.append(getCalltimeChangeComparator(condition.getComparator()))
.append(" by at least ")
.append(formattedThreshold)
.append("]");
if (condition.getName() != null && condition.getName().length() > 0) {
builder.append(" with call destination matching '")
.append(condition.getName())
.append("'");
}
}
break;
case TRAIT:
builder.append("Trait Change [")
.append(condition.getName())
.append("]");
if (condition.getOption() != null && condition.getOption().length() > 0) {
builder.append(" with trait value matching '")
.append(condition.getOption())
.append("'");
}
break;
case CONTROL:
builder.append("Operation Execution [")
.append(condition.getName())
.append("] with result status [")
.append(condition.getOption())
.append("]");
break;
case RESOURCE_CONFIG:
builder.append("Resource Configuration Change");
break;
case EVENT:
builder.append("Event Detection [")
.append(condition.getName())
.append("]");
if (condition.getOption() != null && condition.getOption().length() > 0) {
builder.append(" with event source matching '")
.append(condition.getOption())
.append("'");
}
break;
case DRIFT:
String configNameRegex = condition.getName();
String pathNameRegex = condition.getOption();
if (configNameRegex == null || configNameRegex.length() == 0) {
if (pathNameRegex == null || pathNameRegex.length() == 0) {
// neither a config name regex nor path regex was specified
builder.append("Drift Detection");
} else {
// a path name regex was specified, but not a config name regex
builder.append("Drift Detection for files that match \"")
.append(pathNameRegex)
.append("\"");
}
} else {
if (pathNameRegex == null || pathNameRegex.length() == 0) {
// a config name regex was specified, but not a path name regex
builder.append("Drift Detection for drift definition [")
.append(configNameRegex)
.append("]");
} else {
// both a config name regex and a path regex was specified
builder.append("Drift Detection for files that match \"")
.append(pathNameRegex)
.append("\" and for drift detection [")
.append(configNameRegex)
.append("]");
}
}
break;
case RANGE:
String metricName = condition.getName();
MeasurementUnits metricUnits = condition.getMeasurementDefinition().getUnits();
double loValue = condition.getThreshold();
String formattedLoValue = MeasurementConverter.format(loValue, metricUnits, true);
String formattedHiValue = condition.getOption();
try {
double hiValue = Double.parseDouble(formattedHiValue);
formattedHiValue = MeasurementConverter.format(hiValue, metricUnits, true);
} catch (Exception e) {
formattedHiValue = "?[" + formattedHiValue + "]?"; // signify something is wrong with the value
}
// < means "inside the range", > means "outside the range" - exclusive
// <= means "inside the range", >= means "outside the range" - inclusive
if (condition.getComparator().equals("<")) {
// Metric Value Range: [{0}] between [{1}] and [{2}], exclusive
builder.append("Metric Value Range: [")
.append(metricName)
.append("] between ")
.append(formattedLoValue)
.append("] and [")
.append(formattedHiValue)
.append("], exclusive");
} else if (condition.getComparator().equals(">")) {
// Metric Value Range: [{0}] outside [{1}] and [{2}], exclusive
builder.append("Metric Value Range: [")
.append(metricName)
.append("] outside [")
.append(formattedLoValue)
.append("] and [")
.append(formattedHiValue)
.append("], exclusive");
} else if (condition.getComparator().equals("<=")) {
// Metric Value Range: [{0}] between [{1}] and [{2}], inclusive
builder.append("Metric Value Range: [")
.append(metricName)
.append("] between [")
.append(formattedLoValue)
.append("] and [")
.append(formattedHiValue)
.append("], inclusive");
} else if (condition.getComparator().equals(">=")) {
// Metric Value Range: [{0}] outside [{1}] and [{2}], inclusive
builder.append("Metric Value Range: [")
.append(metricName)
.append("] outside [")
.append(formattedLoValue)
.append("] and [")
.append(formattedHiValue)
.append("], inclusive");
} else {
builder.append("BAD COMPARATOR! Report this bug: ").append(condition.getComparator());
}
break;
default:
// Invalid condition category - please report this as a bug: {0}
builder.append("Invalid condition category - please report this as a bug: ")
.append(category.getName());
}
return builder.toString();
}
private String getCalltimeChangeComparator(String comparator) {
if ("HI".equals(comparator)) {
return "Grows";
} else if ("LO".equals(comparator)) {
return "Shrinks";
} else { // CH
return "Changes";
}
}
};
}
}