/*
* RHQ Management Platform
* Copyright 2013, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.plugins.alertdef;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.alert.AlertCondition;
import org.rhq.core.domain.alert.AlertConditionCategory;
import org.rhq.core.domain.alert.AlertDampening;
import org.rhq.core.domain.alert.AlertDampening.TimeUnits;
import org.rhq.core.domain.alert.AlertDefinition;
import org.rhq.core.domain.alert.AlertPriority;
import org.rhq.core.domain.alert.BooleanExpression;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.AlertDefinitionCriteria;
import org.rhq.core.domain.criteria.ResourceTypeCriteria;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.operation.OperationRequestStatus;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.alert.AlertDefinitionManagerLocal;
import org.rhq.enterprise.server.alert.AlertTemplateManagerLocal;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerLocal;
import org.rhq.enterprise.server.plugin.pc.ControlFacet;
import org.rhq.enterprise.server.plugin.pc.ControlResults;
import org.rhq.enterprise.server.plugin.pc.ServerPluginComponent;
import org.rhq.enterprise.server.plugin.pc.ServerPluginContext;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.util.LookupUtil;
/**
* An alert definition server-side plugin component that the server uses to inject "factory-installed" alert definitions.
*
* @author Jay Shaughnessy
*/
public class AlertDefinitionServerPluginComponent implements ServerPluginComponent, ControlFacet {
private final Log log = LogFactory.getLog(AlertDefinitionServerPluginComponent.class);
private static final String DATA_DISK_USED_PERCENTAGE_METRIC_NAME = "Calculated.DataDiskUsedPercentage";
private static final String TOTAL_DISK_USED_PERCENTAGE_METRIC_NAME = "Calculated.TotalDiskUsedPercentage";
private static final String FREE_DISK_TO_DATA_SIZE_RATIO_METRIC_NAME = "Calculated.FreeDiskToDataSizeRatio";
private static final String TAKE_SNAPSHOT_OPERATION_NAME = "takeSnapshot";
private static final String[] MAINTENANCE_OPERATIONS = new String[] { "readRepair", "addNodeMaintenance",
"removeNodeMaintenance", "announce", "unannounce", "prepareForBootstrap", "prepareForUpgrade",
"updateSeedsList", "updateConfiguration", "takeSnapshot" };
static private final List<InjectedTemplate> injectedTemplates;
static private final InjectedTemplate storageNodeHighHeapTemplate;
static private final InjectedTemplate storageNodeHighDiskUsageTemplate;
static private final InjectedTemplate storageNodeSnapshotFailureTemplate;
static private final InjectedTemplate storageNodeMaintenanceOperationsFailureTemplate;
static {
storageNodeHighHeapTemplate = new InjectedTemplate(
"RHQStorage", //
"VM Memory System", //
"StorageNodeHighHeap", //
"Increase the storage node heap. If you cannot increase the heap or if you continue receiving alerts, then" +
" deploy another storage node. Consult <a href='.#Help/Section1/Section1Item4'>wiki</a> for more info.");
storageNodeHighDiskUsageTemplate = new InjectedTemplate(
"RHQStorage", //
"StorageService", //
"StorageNodeHighDiskUsage", //
"Increase the storage node disk size. If you cannot increase the disk size or if you continue receiving " +
"alerts, then deploy another storage node. Consult <a href='.#Help/Section1/Section1Item4'>wiki</a> for more info.");
storageNodeSnapshotFailureTemplate = new InjectedTemplate(
"RHQStorage", //
"StorageService", //
"StorageNodeSnapshotFailure", //
"Snapshot operation failed for an RHQ Storage Node. When fired please see documentation for the proper corrective action.");
storageNodeMaintenanceOperationsFailureTemplate = new InjectedTemplate(
"RHQStorage", //
"RHQ Storage Node", //
"StorageNodeMaintenanceOperationsFailure", //
"Maintenance operation failed for an RHQ Storage Node. When fired please see documentation for the proper corrective action.");
injectedTemplates = new ArrayList<InjectedTemplate>();
injectedTemplates.add(storageNodeHighHeapTemplate);
injectedTemplates.add(storageNodeHighDiskUsageTemplate);
injectedTemplates.add(storageNodeSnapshotFailureTemplate);
injectedTemplates.add(storageNodeMaintenanceOperationsFailureTemplate);
}
private ServerPluginContext context;
@Override
public void initialize(ServerPluginContext context) throws Exception {
this.context = context;
log.debug("The RHQ AlertDefinition plugin has been initialized!!! : " + this);
}
@Override
public void start() {
boolean injectAtPluginStartup = Boolean.valueOf(context.getPluginConfiguration().getSimpleValue(
"injectAtPluginStartup", "true"));
boolean replaceIfExists = Boolean.valueOf(context.getPluginConfiguration().getSimpleValue("replaceIfExists",
"false"));
if (injectAtPluginStartup) {
injectAllAlertDefs(replaceIfExists);
}
log.debug("The RHQ AlertDefinition plugin has started!!! : " + this);
}
@Override
public void stop() {
log.debug("The RHQ AlertDefinition plugin has stopped!!! : " + this);
}
@Override
public void shutdown() {
log.debug("The RHQ AlertDefinition plugin has been shut down!!! : " + this);
}
@Override
public ControlResults invoke(String name, Configuration parameters) {
ControlResults controlResults = new ControlResults();
try {
if (name.equals("listInjectedAlertDefinitions")) {
PropertyList result = new PropertyList("injectedAlertDefinitions");
for (InjectedTemplate iad : injectedTemplates) {
PropertyMap map = new PropertyMap("injectedAlertDefinition");
map.put(new PropertySimple(InjectedTemplate.FIELD_PLUGIN_NAME, iad.getPluginName()));
map.put(new PropertySimple(InjectedTemplate.FIELD_RESOURCE_TYPE_NAME, iad.getResourceTypeName()));
map.put(new PropertySimple(InjectedTemplate.FIELD_NAME, iad.getName()));
map.put(new PropertySimple(InjectedTemplate.FIELD_DESCRIPTION, iad.getDescription()));
result.add(map);
}
controlResults.getComplexResults().put(result);
} else if (name.equals("injectAllAlertDefinitions")) {
injectAllAlertDefs(Boolean.valueOf(parameters.getSimpleValue("replaceIfExists")));
} else if (name.equals("injectAlertDefinition")) {
InjectedTemplate requestedInjection = new InjectedTemplate( //
parameters.getSimpleValue(InjectedTemplate.FIELD_PLUGIN_NAME), //
parameters.getSimpleValue(InjectedTemplate.FIELD_RESOURCE_TYPE_NAME), //
parameters.getSimpleValue(InjectedTemplate.FIELD_NAME), null);
boolean injected = false;
for (InjectedTemplate iad : injectedTemplates) {
if (iad.equals(requestedInjection)) {
injectAlertDef(iad, Boolean.valueOf(parameters.getSimpleValue("replaceIfExists", "false")));
injected = true;
break;
}
}
if (!injected) {
controlResults
.setError("Unknown requested alert definition. Check spelling: " + requestedInjection);
}
} else {
controlResults.setError("Unknown control name: " + name);
}
} catch (Throwable t) {
controlResults.setError(t);
}
return controlResults;
}
private List<AlertDefinition> injectAllAlertDefs(boolean replaceIfExists) {
List<AlertDefinition> result = new ArrayList<AlertDefinition>();
for (InjectedTemplate iad : injectedTemplates) {
AlertDefinition newAlertDef = injectAlertDef(iad, replaceIfExists);
if (null != newAlertDef) {
result.add(newAlertDef);
}
}
return result;
}
private AlertDefinition injectAlertDef(InjectedTemplate injectedAlertDef, boolean replaceIfExists) {
AlertDefinition result = null;
ResourceTypeManagerLocal typeManager = LookupUtil.getResourceTypeManager();
AlertDefinitionManagerLocal alertDefManager = LookupUtil.getAlertDefinitionManager();
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
ResourceTypeCriteria rtc = new ResourceTypeCriteria();
rtc.addFilterPluginName(injectedAlertDef.getPluginName());
rtc.addFilterName(injectedAlertDef.getResourceTypeName());
rtc.fetchMetricDefinitions(true);
List<ResourceType> resourceTypes = typeManager.findResourceTypesByCriteria(subjectManager.getOverlord(), rtc);
if (resourceTypes.isEmpty()) {
return result;
}
assert 1 == resourceTypes.size() : "Found more than 1 resource type!";
ResourceType resourceType = resourceTypes.get(0);
AlertDefinitionCriteria adc = new AlertDefinitionCriteria();
adc.addFilterName(injectedAlertDef.getName());
adc.addFilterAlertTemplateResourceTypeId(resourceType.getId());
List<AlertDefinition> alertDefs = alertDefManager.findAlertDefinitionsByCriteria(subjectManager.getOverlord(),
adc);
if (!alertDefs.isEmpty()) {
assert 1 == alertDefs.size() : "Found more than 1 existing alert def!";
if (!replaceIfExists) {
return result;
}
int[] alertDefIdArray = new int[1];
alertDefIdArray[0] = alertDefs.get(0).getId();
alertDefManager.removeAlertDefinitions(subjectManager.getOverlord(), alertDefIdArray);
}
int newAlertDefId = 0;
if (storageNodeHighHeapTemplate.equals(injectedAlertDef)) {
newAlertDefId = injectStorageNodeHighHeapTemplate(resourceType);
} else if (storageNodeHighDiskUsageTemplate.equals(injectedAlertDef)) {
newAlertDefId = injectStorageNodeHighDiskUsageTemplate(resourceType);
} else if (storageNodeSnapshotFailureTemplate.equals(injectedAlertDef)) {
newAlertDefId = injectStorageNodeSnapshotFailureTemplate(resourceType);
} else if (storageNodeMaintenanceOperationsFailureTemplate.equals(injectedAlertDef)) {
newAlertDefId = injectStorageNodeMaintenanceOperationsFailureTemplate(resourceType);
}
adc.addFilterId(newAlertDefId);
alertDefs = alertDefManager.findAlertDefinitionsByCriteria(subjectManager.getOverlord(), adc);
assert 1 == alertDefs.size() : "Found more than 1 new alert def!";
result = alertDefs.get(0);
return result;
}
private int injectStorageNodeHighHeapTemplate(ResourceType resourceType) {
AlertTemplateManagerLocal alertTemplateManager = LookupUtil.getAlertTemplateManager();
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
AlertDefinition newTemplate = new AlertDefinition();
newTemplate.setName(storageNodeHighHeapTemplate.getName());
newTemplate.setResourceType(resourceType);
newTemplate.setPriority(AlertPriority.MEDIUM);
newTemplate.setConditionExpression(BooleanExpression.ANY);
newTemplate.setDescription(storageNodeHighHeapTemplate.getDescription());
newTemplate.setRecoveryId(0);
newTemplate.setEnabled(true);
AlertCondition ac = new AlertCondition();
ac.setCategory(AlertConditionCategory.THRESHOLD);
ac.setComparator(">");
ac.setThreshold(0.75D);
List<Integer> measurementDefinitionIds = new ArrayList<Integer>(2);
for (MeasurementDefinition d : resourceType.getMetricDefinitions()) {
if ("Calculated.HeapUsagePercentage".equals(d.getName())) {
measurementDefinitionIds.add(d.getId());
ac.setMeasurementDefinition(d);
ac.setName(d.getDisplayName());
} else if ("{HeapMemoryUsage.used}".equals(d.getName())) {
measurementDefinitionIds.add(d.getId());
}
}
assert null != ac.getMeasurementDefinition() : "Did not find expected measurement definition [Calculated.HeapUsagePercentage] for "
+ resourceType;
newTemplate.addCondition(ac);
AlertDampening dampener = new AlertDampening(AlertDampening.Category.PARTIAL_COUNT);
dampener.setPeriod(15);
dampener.setPeriodUnits(TimeUnits.MINUTES);
dampener.setValue(10);
newTemplate.setAlertDampening(dampener);
int newTemplateId = alertTemplateManager.createAlertTemplate(subjectManager.getOverlord(), newTemplate,
resourceType.getId());
// additionally, we want to ensure that the metric is enabled and collecting at a more frequent interval than
// is set by default.
MeasurementScheduleManagerLocal measurementManager = LookupUtil.getMeasurementScheduleManager();
measurementManager.updateDefaultCollectionIntervalAndEnablementForMeasurementDefinitions(
subjectManager.getOverlord(), ArrayUtils.toPrimitive(measurementDefinitionIds.toArray(new Integer[2])),
60000L, true, true);
return newTemplateId;
}
private int injectStorageNodeHighDiskUsageTemplate(ResourceType resourceType) {
AlertTemplateManagerLocal alertTemplateManager = LookupUtil.getAlertTemplateManager();
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
AlertDefinition newTemplate = new AlertDefinition();
newTemplate.setName(storageNodeHighDiskUsageTemplate.getName());
newTemplate.setResourceType(resourceType);
newTemplate.setPriority(AlertPriority.MEDIUM);
newTemplate.setConditionExpression(BooleanExpression.ANY);
newTemplate.setDescription(storageNodeHighDiskUsageTemplate.getDescription());
newTemplate.setRecoveryId(0);
newTemplate.setEnabled(true);
AlertCondition dataDiskUsedAlertCondition = new AlertCondition();
dataDiskUsedAlertCondition.setCategory(AlertConditionCategory.THRESHOLD);
dataDiskUsedAlertCondition.setComparator(">");
dataDiskUsedAlertCondition.setThreshold(0.5D);
AlertCondition totalDiskUsedAlertCondition = new AlertCondition();
totalDiskUsedAlertCondition.setCategory(AlertConditionCategory.THRESHOLD);
totalDiskUsedAlertCondition.setComparator(">");
totalDiskUsedAlertCondition.setThreshold(0.75D);
AlertCondition freeSpaveDataRatioAlertCondition = new AlertCondition();
freeSpaveDataRatioAlertCondition.setCategory(AlertConditionCategory.THRESHOLD);
freeSpaveDataRatioAlertCondition.setComparator("<");
freeSpaveDataRatioAlertCondition.setThreshold(1.5D);
List<Integer> measurementDefinitionIds = new ArrayList<Integer>(1);
for (MeasurementDefinition d : resourceType.getMetricDefinitions()) {
if (DATA_DISK_USED_PERCENTAGE_METRIC_NAME.equals(d.getName())) {
measurementDefinitionIds.add(d.getId());
dataDiskUsedAlertCondition.setMeasurementDefinition(d);
dataDiskUsedAlertCondition.setName(d.getDisplayName());
} else if (TOTAL_DISK_USED_PERCENTAGE_METRIC_NAME.equals(d.getName())) {
measurementDefinitionIds.add(d.getId());
totalDiskUsedAlertCondition.setMeasurementDefinition(d);
totalDiskUsedAlertCondition.setName(d.getDisplayName());
} else if (FREE_DISK_TO_DATA_SIZE_RATIO_METRIC_NAME.equals(d.getName())) {
measurementDefinitionIds.add(d.getId());
freeSpaveDataRatioAlertCondition.setMeasurementDefinition(d);
freeSpaveDataRatioAlertCondition.setName(d.getDisplayName());
}
}
newTemplate.addCondition(dataDiskUsedAlertCondition);
newTemplate.addCondition(totalDiskUsedAlertCondition);
newTemplate.addCondition(freeSpaveDataRatioAlertCondition);
AlertDampening dampener = new AlertDampening(AlertDampening.Category.PARTIAL_COUNT);
dampener.setPeriod(15);
dampener.setPeriodUnits(TimeUnits.MINUTES);
dampener.setValue(10);
newTemplate.setAlertDampening(dampener);
int newTemplateId = alertTemplateManager.createAlertTemplate(subjectManager.getOverlord(), newTemplate,
resourceType.getId());
// additionally, we want to ensure that the metric is enabled and collecting at a more frequent interval than
// is set by default.
MeasurementScheduleManagerLocal measurementManager = LookupUtil.getMeasurementScheduleManager();
measurementManager.updateDefaultCollectionIntervalAndEnablementForMeasurementDefinitions(
subjectManager.getOverlord(),
ArrayUtils.toPrimitive(measurementDefinitionIds.toArray(new Integer[measurementDefinitionIds.size()])),
60000L, true, true);
return newTemplateId;
}
private int injectStorageNodeSnapshotFailureTemplate(ResourceType resourceType) {
AlertTemplateManagerLocal alertTemplateManager = LookupUtil.getAlertTemplateManager();
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
AlertDefinition newTemplate = new AlertDefinition();
newTemplate.setName(storageNodeSnapshotFailureTemplate.getName());
newTemplate.setResourceType(resourceType);
newTemplate.setPriority(AlertPriority.MEDIUM);
newTemplate.setConditionExpression(BooleanExpression.ANY);
newTemplate.setDescription(storageNodeSnapshotFailureTemplate.getDescription());
newTemplate.setRecoveryId(0);
newTemplate.setEnabled(true);
AlertCondition snapshotFailureCondition = new AlertCondition();
snapshotFailureCondition.setCategory(AlertConditionCategory.CONTROL);
snapshotFailureCondition.setName(TAKE_SNAPSHOT_OPERATION_NAME);
snapshotFailureCondition.setOption(OperationRequestStatus.FAILURE.name());
newTemplate.addCondition(snapshotFailureCondition);
AlertDampening dampener = new AlertDampening(AlertDampening.Category.NONE);
newTemplate.setAlertDampening(dampener);
int newTemplateId = alertTemplateManager.createAlertTemplate(subjectManager.getOverlord(), newTemplate,
resourceType.getId());
return newTemplateId;
}
private int injectStorageNodeMaintenanceOperationsFailureTemplate(ResourceType resourceType) {
AlertTemplateManagerLocal alertTemplateManager = LookupUtil.getAlertTemplateManager();
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
AlertDefinition newTemplate = new AlertDefinition();
newTemplate.setName(storageNodeMaintenanceOperationsFailureTemplate.getName());
newTemplate.setResourceType(resourceType);
newTemplate.setPriority(AlertPriority.MEDIUM);
newTemplate.setConditionExpression(BooleanExpression.ANY);
newTemplate.setDescription(storageNodeMaintenanceOperationsFailureTemplate.getDescription());
newTemplate.setRecoveryId(0);
newTemplate.setEnabled(true);
for (String operation : MAINTENANCE_OPERATIONS) {
AlertCondition snapshotFailureCondition = new AlertCondition();
snapshotFailureCondition.setCategory(AlertConditionCategory.CONTROL);
snapshotFailureCondition.setName(operation);
snapshotFailureCondition.setOption(OperationRequestStatus.FAILURE.name());
newTemplate.addCondition(snapshotFailureCondition);
}
AlertDampening dampener = new AlertDampening(AlertDampening.Category.NONE);
newTemplate.setAlertDampening(dampener);
int newTemplateId = alertTemplateManager.createAlertTemplate(subjectManager.getOverlord(), newTemplate,
resourceType.getId());
return newTemplateId;
}
private static class InjectedTemplate {
static public final String FIELD_PLUGIN_NAME = "plugin";
static public final String FIELD_RESOURCE_TYPE_NAME = "type";
static public final String FIELD_NAME = "name";
static public final String FIELD_DESCRIPTION = "description";
private String pluginName;
private String resourceTypeName;
private String name;
private String description;
public InjectedTemplate(String pluginName, String resourceTypeName, String name, String description) {
super();
this.pluginName = pluginName;
this.resourceTypeName = resourceTypeName;
this.name = name;
this.description = description;
}
public String getPluginName() {
return pluginName;
}
public String getResourceTypeName() {
return resourceTypeName;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((pluginName == null) ? 0 : pluginName.hashCode());
result = prime * result + ((resourceTypeName == null) ? 0 : resourceTypeName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InjectedTemplate other = (InjectedTemplate) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (pluginName == null) {
if (other.pluginName != null)
return false;
} else if (!pluginName.equals(other.pluginName))
return false;
if (resourceTypeName == null) {
if (other.resourceTypeName != null)
return false;
} else if (!resourceTypeName.equals(other.resourceTypeName))
return false;
return true;
}
@Override
public String toString() {
return "InjectedAlertDef [pluginName=" + pluginName + ", resourceTypeName=" + resourceTypeName + ", name="
+ name + "]";
}
}
}