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"; } } }; } }