/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2007], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.measurement.galerts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.appdef.galerts.ResourceAuxLog;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.AppdefEntityValue;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.ResourceGroup;
import org.hyperic.hq.authz.shared.AuthzSubjectManager;
import org.hyperic.hq.authz.shared.ResourceGroupManager;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.events.SimpleAlertAuxLog;
import org.hyperic.hq.galerts.processor.FireReason;
import org.hyperic.hq.galerts.processor.Gtrigger;
import org.hyperic.hq.galerts.server.session.ExecutionStrategy;
import org.hyperic.hq.measurement.TimingVoodoo;
import org.hyperic.hq.measurement.server.session.Measurement;
import org.hyperic.hq.measurement.server.session.MeasurementScheduleZevent;
import org.hyperic.hq.measurement.server.session.MeasurementTemplate;
import org.hyperic.hq.measurement.server.session.MeasurementZevent;
import org.hyperic.hq.measurement.server.session.MeasurementScheduleZevent.MeasurementScheduleZeventSource;
import org.hyperic.hq.measurement.server.session.MeasurementZevent.MeasurementZeventPayload;
import org.hyperic.hq.measurement.server.session.MeasurementZevent.MeasurementZeventSource;
import org.hyperic.hq.measurement.shared.MeasurementManager;
import org.hyperic.hq.measurement.shared.TemplateManager;
import org.hyperic.hq.product.MetricValue;
import org.hyperic.hq.zevents.HeartBeatZevent;
import org.hyperic.hq.zevents.Zevent;
import org.hyperic.hq.zevents.HeartBeatZevent.HeartBeatZeventSource;
/**
* This is a simple trigger which will fire when the criteria is met, and
* not-fire when the criteria is .. not met.
*/
public class MeasurementGtrigger
extends Gtrigger
{
private static final Log _log = LogFactory.getLog(MeasurementGtrigger.class);
/**
* We need to allow up to a 1 minute time skew with the agent behind the
* server.
*/
private static final int DEFAULT_AGENT_SERVER_TIME_SKEW=60 * 1000;
/**
* The minimum assumed measurement collection interval. This
* value doesn't have to be exact since it's only used to
* estimate the time window if we have the case where none
* of the resources in the group are collecting on the metric.
*/
private static final int DEFAULT_MIN_COLLECTION_INTERVAL= 60 * 1000;
private final SizeComparator _sizeCompare;
private final int _numResources; // Num needed to match
private final boolean _isPercent;
private final Integer _templateId;
private final ComparisonOperator _comparator;
private final Float _metricVal;
private final Set _interestedEvents;
private final Map _trackedResources;
private final boolean _isNotReportingEventsOffending;
private final TreeSet _trackedHeartBeatTimestamps;
private String _triggerName;
private String _partitionDescription;
private long minCollectionInterval;
private long _maxCollectionInterval;
private long timeSkew;
private boolean _isWithinFirstTimeWindow;
private boolean _isTimeWindowingInitialized;
private long _startOfTimeWindowExact; // the start of the time window
private long _startOfTimeWindow; // the start of the time window, voodooed down
private String _metricName;
private int _groupSize; // The total size of our group
private ResourceGroup _resourceGroup;
// These are the resources that have the metric collection interval set
private final Map _srcId2CollectionInterval;
MeasurementGtrigger(SizeComparator sizeCompare,
int numResources,
boolean isPercent,
int templateId,
ComparisonOperator comparator,
float metricVal,
boolean isNotReportingOffending)
{
_sizeCompare = sizeCompare;
_numResources = numResources;
_isPercent = isPercent;
_templateId = new Integer(templateId);
_comparator = comparator;
_metricVal = new Float(metricVal);
_interestedEvents = new HashSet();
_trackedResources = new HashMap();
_metricName = "Unknown";
_groupSize = 0;
_isNotReportingEventsOffending = isNotReportingOffending;
_srcId2CollectionInterval = new HashMap();
_trackedHeartBeatTimestamps = new TreeSet();
this.minCollectionInterval = DEFAULT_MIN_COLLECTION_INTERVAL;
_maxCollectionInterval = minCollectionInterval;
this.timeSkew = DEFAULT_AGENT_SERVER_TIME_SKEW;
_isTimeWindowingInitialized = false;
setTriggerName();
}
public Set getInterestedEvents() {
return Collections.unmodifiableSet(_interestedEvents);
}
public void processEvent(Zevent event) {
// Process measurement schedule changes.
if (isMeasurementScheduleEvent(event)) {
metricCollectionIntervalChanged();
return;
}
initializeTimeWindowing(event);
// Evaluate the sliding time window boundary.
boolean inFirstTimeWindow = isInFirstTimeWindow();
// We only move the time window forward when a heart beat is processed.
if (isHeartBeatEvent(event)) {
evaluateNextStartOfTimeWindow(inFirstTimeWindow);
}
long endOfTimeWindow = _startOfTimeWindow+2*_maxCollectionInterval;
// Track heart beat events
if (isHeartBeatEvent(event)) {
HeartBeatZevent hb = (HeartBeatZevent)event;
if (!isOlderThanTimeWindowStartTime(hb, _startOfTimeWindow)) {
track(hb);
}
}
// Track the measurement events.
if (isMeasurementEvent(event)) {
MeasurementZevent me = (MeasurementZevent)event;
if (!isOlderThanTimeWindowStartTime(me, _startOfTimeWindow)) {
track(me);
}
return;
}
if (!shouldStartEvaluatingMetrics()) {
return;
}
// Try to fire.
Map srcId2ViolatingMetricValue =
evaluateResourceMetricsAndReturnViolators(_trackedResources,
_startOfTimeWindow,
endOfTimeWindow);
tryToFire(srcId2ViolatingMetricValue, _startOfTimeWindow, endOfTimeWindow);
}
/**
* Initialize the time windowing. This is only done once when the trigger
* processes the first heart beat or measurement event.
*
* @param event The event.
*/
private void initializeTimeWindowing(Zevent event) {
if (!_isTimeWindowingInitialized &&
(isHeartBeatEvent(event) || isMeasurementEvent(event))) {
setStartOfFirstTimeWindow(System.currentTimeMillis());
_isTimeWindowingInitialized = true;
}
}
/**
* Is this a heart beat event?
*
* @param event The event.
* @return <code>true</code> if this is a {@link HeartBeatZevent}.
*/
private boolean isHeartBeatEvent(Zevent event) {
return event instanceof HeartBeatZevent;
}
/**
* Is this a measurement schedule event?
*
* @param event The event.
* @return <code>true</code> if this is a {@link MeasurementScheduleZevent}.
*/
private boolean isMeasurementScheduleEvent(Zevent event) {
return event instanceof MeasurementScheduleZevent;
}
/**
* Is this a measurement event?
*
* @param event The event.
* @return <code>true</code> if this is a {@link MeasurementZevent}.
*/
private boolean isMeasurementEvent(Zevent event) {
return event instanceof MeasurementZevent;
}
/**
* Process the collection interval change.
*/
private void metricCollectionIntervalChanged() {
// It's safer to pull directly from the database than to
// use the measurement schedule event, since we may have
// missed a prior measurement schedule event.
List derivedMeas = getMeasurementsCollecting();
// Find resources that are just starting to collect and create
// a resource metric tracker for those resources.
for (Iterator iter = derivedMeas.iterator(); iter.hasNext();) {
Measurement meas = (Measurement) iter.next();
int mid = meas.getId().intValue();
MeasurementZeventSource srcId = new MeasurementZeventSource(mid);
if (!_srcId2CollectionInterval.containsKey(srcId)) {
_log.debug("Start tracking newly scheduled measurement " +
"for trigger ["+getTriggerNameWithPartitionDesc()+
"]: "+srcId);
getResourceTrackerAddIfNecessary(srcId);
}
}
// Now we can rebuild the collection interval map and reset the
// max collection interval.
_srcId2CollectionInterval.clear();
long oldInterval = _maxCollectionInterval;
_maxCollectionInterval = minCollectionInterval;
for (Iterator iter = derivedMeas.iterator(); iter.hasNext();) {
Measurement meas = (Measurement) iter.next();
int mid = meas.getId().intValue();
Long interval = new Long(meas.getInterval());
_srcId2CollectionInterval.put(
new MeasurementZeventSource(mid), interval);
_maxCollectionInterval =
Math.max(_maxCollectionInterval, meas.getInterval());
}
_log.debug("Trigger ["+getTriggerNameWithPartitionDesc()+
"] processed measurement schedule zevent: old collection interval="+
oldInterval+"; new collection interval="+_maxCollectionInterval);
}
/**
* @return <code>true</code> if we are still in the first time window.
*/
private boolean isInFirstTimeWindow() {
if (_isWithinFirstTimeWindow) {
if (System.currentTimeMillis() <
_startOfTimeWindowExact+(2*_maxCollectionInterval)+
timeSkew) {
// still in first time window
_isWithinFirstTimeWindow = true;
} else {
_isWithinFirstTimeWindow = false;
}
}
return _isWithinFirstTimeWindow;
}
/**
* If we are in the first time window, then the start time doesn't change
* from the previous value. Otherwise, the start of the time window is the
* timestamp of the oldest tracked heart beat (or the current time if no
* heart beats are currently being tracked).
*
* @param inFirstTimeWindow <code>true</code> if we are in the first time window.
*/
private void evaluateNextStartOfTimeWindow(boolean inFirstTimeWindow) {
boolean debug = _log.isDebugEnabled();
if (inFirstTimeWindow) {
// don't change start of time window yet - use the old value
} else {
if (_trackedHeartBeatTimestamps.isEmpty() ||
_trackedHeartBeatTimestamps.first() == null) {
// The next start of time window is unknown! It's as though we
// are in the very first time window.
if (debug) {
_log.debug("The next start of time window is unknown! " +
"Resetting time window for trigger ["+
getTriggerNameWithPartitionDesc()+"]");
}
setStartOfFirstTimeWindow(System.currentTimeMillis());
} else {
Long timestamp = (Long)_trackedHeartBeatTimestamps.first();
_trackedHeartBeatTimestamps.remove(timestamp);
// Make sure we don't move back in time!
_startOfTimeWindow =
Math.max(_startOfTimeWindow, timestamp.longValue());
}
}
if (debug) {
_log.debug("Start of time window for trigger ["+
getTriggerNameWithPartitionDesc()+"] ="+
_startOfTimeWindow+", in first time window="+
_isWithinFirstTimeWindow);
}
}
/**
* When determining if we've moved beyond the first time window, use the
* exact time. When calculating the time window lower bound use the voodooed
* time, since the measurement and heart beat event timestamps are voodooed.
*
* @param timestamp The timestamp.
*/
private void setStartOfFirstTimeWindow(long timestamp) {
_isWithinFirstTimeWindow = true;
_startOfTimeWindowExact = timestamp;
// Even if we are timing voodooing down, we don't want to move back
// in time. Use the max of the last start of time window and the
// new value. Timing voodoo down to the nearest 30 seconds since
// this is the finest resolution we have (for heart beat events).
_startOfTimeWindow = Math.max(_startOfTimeWindow,
TimingVoodoo.roundDownTime(timestamp,
HeartBeatZevent.HEART_BEAT_INTERVAL_MILLIS));
}
/**
* @return <code>true</code> if we should start evaluating the tracked metrics.
*/
private boolean shouldStartEvaluatingMetrics() {
// If we are in the first time window, then give the trigger through
// the end of that time window to track metric history without
// evaluating metrics. That way, we'll know if a resource that should
// be reporting isn't actually reporting.
return _isWithinFirstTimeWindow == false;
}
/**
* Is this measurement event older than the start of the time window?
*
* @param event The measurement event.
* @param startTime The start timestamp for the time window.
* @return <code>true</code> if this is an old measurement event.
*/
private boolean isOlderThanTimeWindowStartTime(MeasurementZevent event,
long startTime) {
MeasurementZeventPayload payload =
(MeasurementZeventPayload)event.getPayload();
MetricValue val = payload.getValue();
if (val.getTimestamp() < startTime) {
if (_log.isDebugEnabled()) {
_log.debug("Trigger ["+getTriggerNameWithPartitionDesc()+
"] is rejecting measurement event older than time window: "+
event+"; "+val.getTimestamp()+" < "+startTime);
}
return true;
} else {
return false;
}
}
/**
* Is this heart beat event older than the start of the time window?
*
* @param event The heart beat event.
* @param startTime The start timestamp for the time window.
* @return <code>true</code> if this is an old heart beat event.
*/
private boolean isOlderThanTimeWindowStartTime(HeartBeatZevent event,
long startTime) {
if (event.getVoodooedTimestamp() < startTime) {
if (_log.isDebugEnabled()) {
_log.debug("Trigger ["+getTriggerNameWithPartitionDesc()+
"] is rejecting heart beat event older than time window: "+
event+"; "+event.getVoodooedTimestamp()+" < "+startTime);
}
return true;
} else {
return false;
}
}
/**
* Track this heart beat event. Heart beats are used to move the time
* window forward since they are timestamped "reliably" every 30 minutes,
* less than the minimum measurement collection interval. Also, since
* heart beats share the same zevent queue as the measurement events, if
* they are processed slower by the trigger, the time window will move
* forward slower. In that respect, they serve to throttle the time
* window velocity in case the system is bogged down, minimizing the
* loss of measurement events that may spend a long time in the zevent
* queue.
*
* @param event The heart beat event.
*/
private void track(HeartBeatZevent event) {
_trackedHeartBeatTimestamps.add(new Long(event.getVoodooedTimestamp()));
if (_log.isDebugEnabled()) {
_log.debug("Tracking heart beat for trigger ["+
getTriggerNameWithPartitionDesc()+"]: "+event);
}
}
/**
* Retrieve the resource metric tracker for a given resource, adding it
* to the map of tracked resources if it does not already exist there.
*
* @param sourceId The measurement source id.
* @return The resource tracker.
*/
private ResourceMetricTracker getResourceTrackerAddIfNecessary(
MeasurementZeventSource sourceId) {
ResourceMetricTracker tracker =
(ResourceMetricTracker)_trackedResources.get(sourceId);
if (tracker==null) {
tracker = new ResourceMetricTracker(_comparator,
_metricVal,
_isNotReportingEventsOffending);
_trackedResources.put(sourceId, tracker);
}
return tracker;
}
/**
* Track this measurement event.
*
* @param event The measurement event.
*/
private void track(MeasurementZevent event) {
MeasurementZeventSource sourceId =
(MeasurementZeventSource)event.getSourceId();
ResourceMetricTracker tracker = getResourceTrackerAddIfNecessary(sourceId);
MeasurementZeventPayload payload =
(MeasurementZeventPayload)event.getPayload();
MetricValue val = payload.getValue();
tracker.trackMetricValue(val);
if (_log.isDebugEnabled()) {
_log.debug("Tracking measurement for trigger ["+
getTriggerNameWithPartitionDesc()+
"]: "+event+", timestamp="+val.getTimestamp());
}
}
/**
* Evaluate the metrics within the time window and return the first found
* violating metric value for each of the tracked resources.
*
* @param trackedResources The tracked resources.
* @param startTime The start timestamp for the time window (inclusive).
* @param endTime The end timestamp for the time window (inclusive).
* @return The map of {@link MeasurementZeventSource}Ids to {@link MetricValue}s.
*/
private Map evaluateResourceMetricsAndReturnViolators(Map trackedResources,
long startTime,
long endTime) {
boolean debug = _log.isDebugEnabled();
Map srcId2MetricValue = new HashMap();
if (debug) {
_log.debug("Checking for violating measurements for trigger ["+
getTriggerNameWithPartitionDesc()+"] with time window; start="+
startTime+", end="+endTime);
}
for (Iterator iter = trackedResources.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
MeasurementZeventSource src = (MeasurementZeventSource)entry.getKey();
ResourceMetricTracker tracker = (ResourceMetricTracker)entry.getValue();
// Remove resources that are not scheduled to collect and don't
// have any remaining tracked metrics so we don't accidentally
// consider them violating the trigger conditions (if non reporting
// resources are considered violating).
if (tracker.getNumberOfTrackedMetrics()==0 &&
!_srcId2CollectionInterval.containsKey(src)) {
if (debug) {
_log.debug("Stopped tracking unscheduled measurement for trigger ["+
getTriggerNameWithPartitionDesc()+"]: "+src);
}
iter.remove();
continue;
}
MetricValue val =
tracker.searchForViolatingMetricInWindow(startTime, endTime);
if (val != null) {
// Make sure the resource hasn't been deleted
Measurement metric =
getDMMan().getMeasurement(new Integer(src.getId()));
if (metric == null ||
metric.getResource() == null ||
metric.getResource().isInAsyncDeleteState()) {
iter.remove();
continue;
}
srcId2MetricValue.put(src, val);
if (debug) {
_log.debug("Found violating measurement for trigger ["+
getTriggerNameWithPartitionDesc()+"]: "+src+
", "+val+", timestamp="+val.getTimestamp());
}
}
}
return srcId2MetricValue;
}
/**
* The alert fired time is the average timestamp for the current time
* window.
*
* @param startTime The start timestamp for the time window (inclusive).
* @param endTime The end timestamp for the time window (exclusive).
* @return The alert fired time.
*/
private long getAlertFiredTime(long startTime, long endTime) {
return (endTime+startTime)/2L;
}
private void tryToFire(Map srcId2MetricValue, long startTime, long endTime) {
if (_groupSize == 0) {
if (_log.isDebugEnabled()) {
_log.debug("Trigger ["+getTriggerNameWithPartitionDesc()+
"] has no resources in its group. Aborting " +
"trigger evaluation.");
}
return;
}
int numMatched = 0;
if (srcId2MetricValue.size()>0) {
for (Iterator i=srcId2MetricValue.values().iterator(); i.hasNext(); ) {
MetricValue val = (MetricValue)i.next();
// Offending resources always add towards the number matched
if (val.equals(MetricValue.NONE) ||
_comparator.isTrue(new Float(val.getValue()), _metricVal)) {
numMatched++;
}
}
}
String leftHandStr, numMatchStr;
int leftHand;
if (_isPercent) {
// Shouldn't be larger than 100%
leftHand = Math.min((numMatched * 100 / _groupSize), 100);
numMatchStr = leftHand + "%";
leftHandStr = _numResources + "%";
} else {
leftHand = numMatched;
numMatchStr = leftHand + "";
leftHandStr = "" + _numResources;
}
if (_log.isDebugEnabled()) {
_log.debug("For trigger ["+getTriggerNameWithPartitionDesc()+
"] checking if "+_sizeCompare+" "+_numResources+
(_isPercent ? "%" : "")+" of the resources reported "+
_metricName+" "+_comparator+" "+_metricVal);
_log.debug("Number of resources matching condition="+
numMatched+", group size="+_groupSize);
}
if (!_sizeCompare.isTrue(leftHand, _numResources)) {
setNotFired();
return;
}
MeasurementTemplate template = Bootstrap.getBean(TemplateManager.class).getTemplate(_templateId);
StringBuffer sr = new StringBuffer();
StringBuffer lr = new StringBuffer();
sr.append(_sizeCompare)
.append(" ")
.append(leftHandStr)
.append(" of the resources reported ")
.append(_metricName)
.append(" ")
.append(_comparator)
.append(" ")
.append(template.formatValue(_metricVal.doubleValue()));
lr.append(_sizeCompare)
.append(" ")
.append(leftHandStr)
.append(" of the resources (")
.append(numMatchStr)
.append(") reported ")
.append(_metricName)
.append(" ")
.append(_comparator)
.append(" ")
.append(template.formatValue(_metricVal.doubleValue()));
long nonReportingResourceFiredTime = getAlertFiredTime(startTime, endTime);
setFired(new FireReason(sr.toString(), lr.toString(),
formulateAuxLogs(srcId2MetricValue, nonReportingResourceFiredTime)));
}
private List formulateAuxLogs(Map srcId2MetricValue,
long nonReportingResourceFiredTime) {
// Assemble the aux info
List auxLogs = new ArrayList();
MeasurementManager dmMan = getDMMan();
AuthzSubject overlord =
Bootstrap.getBean(AuthzSubjectManager.class).getOverlordPojo();
for (Iterator i=srcId2MetricValue.entrySet().iterator(); i.hasNext(); ) {
Map.Entry ent = (Map.Entry)i.next();
MeasurementZeventSource src = (MeasurementZeventSource)ent.getKey();
MetricValue val = (MetricValue)ent.getValue();
SimpleAlertAuxLog baseLog;
AppdefEntityValue entVal;
AppdefEntityID entId;
// We know that offending resources violate the conditions.
// No need to do the comparison in this case.
if (!val.equals(MetricValue.NONE)) {
if (!_comparator.isTrue(new Float(val.getValue()), _metricVal))
continue;
}
Measurement metric =
dmMan.getMeasurement(new Integer(src.getId()));
if (metric == null) {
// HQ-1117: The resource has already been deleted
// Don't consider this resource
continue;
}
entId = new AppdefEntityID(metric.getAppdefType(),
metric.getInstanceId());
entVal = new AppdefEntityValue(entId, overlord);
String entName;
try {
entName = entVal.getName();
} catch(Exception e) {
entName = entId.toString();
}
String metricName = metric.getTemplate().getName();
String descrNoVal = entName+" reported "+metricName+" = ";
String descr;
long timestamp;
// Set the metric description and timestamp in the aux log.
if (val.equals(MetricValue.NONE)) {
descr = descrNoVal+"Unknown";
timestamp = nonReportingResourceFiredTime;
} else {
String formattedValue = metric.getTemplate().formatValue(val);
descr = descrNoVal + formattedValue;
timestamp = val.getTimestamp();
}
baseLog = new SimpleAlertAuxLog(descr, timestamp);
baseLog.addChild(new MetricAuxLog(metricName+" chart", timestamp, metric));
baseLog.addChild(new ResourceAuxLog(entName, timestamp, entId));
auxLogs.add(baseLog);
}
return auxLogs;
}
private MeasurementManager getDMMan() {
return Bootstrap.getBean(MeasurementManager.class);
}
public void setGroup(ResourceGroup rg) {
_resourceGroup = rg;
_interestedEvents.clear();
ResourceGroupManager gMan = Bootstrap.getBean(ResourceGroupManager.class);
AuthzSubjectManager sMan = Bootstrap.getBean(AuthzSubjectManager.class);
TemplateManager tMan = Bootstrap.getBean(TemplateManager.class);
try {
ResourceGroup g = gMan.findResourceGroupById(sMan.getOverlordPojo(),
rg.getId());
_groupSize = gMan.getNumMembers(g);
_log.debug("Resource group set: id="+rg.getId()+", size="+_groupSize);
List derivedMeas = getMeasurementsCollecting();
_maxCollectionInterval = minCollectionInterval;
for (Iterator iter = derivedMeas.iterator(); iter.hasNext();) {
Measurement meas = (Measurement) iter.next();
int mid = meas.getId().intValue();
Long interval = new Long(meas.getInterval());
_maxCollectionInterval =
Math.max(_maxCollectionInterval, meas.getInterval());
MeasurementZeventSource srcId = new MeasurementZeventSource(mid);
_interestedEvents.add(srcId);
_srcId2CollectionInterval.put(srcId, interval);
// HQ-1165: Create a resource metric tracker for this resource
// right now in case we have decided that not reporting resources
// are offending and a metric is never reported for that resource.
// We don't want to depend on receiving at least one metric for
// that resource before we can determine that it isn't reporting.
getResourceTrackerAddIfNecessary(srcId);
MeasurementScheduleZeventSource scheduleSrcId =
new MeasurementScheduleZeventSource(mid);
_interestedEvents.add(scheduleSrcId);
}
_interestedEvents.add(HeartBeatZeventSource.getInstance());
if (derivedMeas.size() != _groupSize) {
_log.warn("Listening to different # measurement events ("+
derivedMeas.size()+") than resources ("+
_groupSize+") for group " + _resourceGroup.getName() +
". Not all resources are collecting " + _metricName);
}
_metricName = tMan.getTemplate(_templateId).getName();
setTriggerName();
} catch(Exception e) {
throw new SystemException(e);
}
}
/**
* Get the measurements collecting for each resource in the group.
*
* @return The list of {@link org.hyperic.hq.measurement.server.session.Measurement} objects.
*/
private List<Measurement> getMeasurementsCollecting() {
return getDMMan().getMetricsCollecting(_resourceGroup, _templateId);
}
private String getTriggerNameWithPartitionDesc() {
if (_partitionDescription == null) {
ExecutionStrategy strategy = getStrategy();
if (strategy != null) {
if (strategy.getPartition() != null) {
_partitionDescription =
strategy.getPartition().getDescription();
}
}
}
if (_partitionDescription != null) {
return _triggerName+" "+_partitionDescription;
} else {
return _triggerName;
}
}
private void setTriggerName() {
_triggerName = getAlertDefName()+" ["+_metricName+" "+_comparator+" "+_metricVal+"]";
}
/**
*
* @param minCollectionInterval The smallest allowable collection interval in ms.
* The start time window will be determined by taking the max of this number
* and the metric collection interval of the MeasurementEvent being processed.
* Default is 1 min.
*/
void setMinCollectionInterval(long minCollectionInterval) {
this.minCollectionInterval = minCollectionInterval;
}
/**
*
* @param timeSkew The amount of time to add to the initial delay in metric processing to account for time lag b/w agent and server in ms.
* Default is 1 minute.
*/
void setTimeSkew(long timeSkew) {
this.timeSkew = timeSkew;
}
}