This is an automated email from the ASF dual-hosted git repository.

aonishuk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 6e6b4a1  AMBARI-23063. "Stale Alerts" alert has to be re-done properly 
(aonishuk)
6e6b4a1 is described below

commit 6e6b4a1d74b46b501c9b071aae1f849502871b3f
Author: Andrew Onishuk <aonis...@hortonworks.com>
AuthorDate: Fri Feb 23 10:24:43 2018 +0200

    AMBARI-23063. "Stale Alerts" alert has to be re-done properly (aonishuk)
---
 .../python/ambari_agent/AlertStatusReporter.py     |   3 +
 .../ambari_agent/ClusterAlertDefinitionsCache.py   |  27 +-
 .../main/python/ambari_agent/HeartbeatThread.py    |  16 +-
 .../main/python/ambari_agent/InitializerModule.py  |   2 +
 .../main/python/ambari_agent/StaleAlertsMonitor.py |  71 ++++
 .../main/python/ambari_agent/alerts/base_alert.py  |   6 +
 .../org/apache/ambari/server/agent/HeartBeat.java  |   7 +
 .../ambari/server/agent/HeartBeatHandler.java      |  53 +--
 .../org/apache/ambari/server/agent/StaleAlert.java |  59 +++
 .../server/agent/stomp/AlertDefinitionsHolder.java |  28 +-
 .../server/agent/stomp/dto/AlertCluster.java       |  30 +-
 .../apache/ambari/server/alerts/AlertRunnable.java |  33 +-
 .../server/alerts/AmbariPerformanceRunnable.java   |   4 +-
 .../ambari/server/alerts/StaleAlertRunnable.java   | 135 +++---
 .../controller/internal/AlertResourceProvider.java |   3 +
 .../alerts/AlertDefinitionsUIUpdateListener.java   |  13 +
 .../listeners/alerts/AlertReceivedListener.java    |  24 +-
 .../org/apache/ambari/server/state/Cluster.java    |   8 +
 .../ambari/server/state/alert/AlertHelper.java     | 191 +++++++++
 .../ambari/server/state/cluster/ClusterImpl.java   |  17 +
 .../ambari/server/alerts/AlertHelperTest.java      |  69 +++
 .../alerts/AmbariPerformanceRunnableTest.java      |  16 -
 .../server/alerts/StaleAlertRunnableTest.java      | 462 ++++++++++++++++-----
 .../state/alerts/AlertReceivedListenerTest.java    |  26 +-
 .../server/state/cluster/AlertDataManagerTest.java |   2 +-
 25 files changed, 1011 insertions(+), 294 deletions(-)

diff --git a/ambari-agent/src/main/python/ambari_agent/AlertStatusReporter.py 
b/ambari-agent/src/main/python/ambari_agent/AlertStatusReporter.py
index 2c65a00..e588bd1 100644
--- a/ambari-agent/src/main/python/ambari_agent/AlertStatusReporter.py
+++ b/ambari-agent/src/main/python/ambari_agent/AlertStatusReporter.py
@@ -38,6 +38,7 @@ class AlertStatusReporter(threading.Thread):
     self.collector = initializer_module.alert_scheduler_handler.collector()
     self.stop_event = initializer_module.stop_event
     self.alert_reports_interval = 
initializer_module.config.alert_reports_interval
+    self.stale_alerts_monitor = initializer_module.stale_alerts_monitor
     self.reported_alerts = defaultdict(lambda:defaultdict(lambda:[]))
     threading.Thread.__init__(self)
 
@@ -53,6 +54,8 @@ class AlertStatusReporter(threading.Thread):
       try:
         if self.initializer_module.is_registered:
           alerts = self.collector.alerts()
+          self.stale_alerts_monitor.save_executed_alerts(alerts)
+
           changed_alerts = self.get_changed_alerts(alerts)
 
           if changed_alerts and self.initializer_module.is_registered:
diff --git 
a/ambari-agent/src/main/python/ambari_agent/ClusterAlertDefinitionsCache.py 
b/ambari-agent/src/main/python/ambari_agent/ClusterAlertDefinitionsCache.py
index e2ed4cf..96e332f 100644
--- a/ambari-agent/src/main/python/ambari_agent/ClusterAlertDefinitionsCache.py
+++ b/ambari-agent/src/main/python/ambari_agent/ClusterAlertDefinitionsCache.py
@@ -18,26 +18,6 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
-#!/usr/bin/env python
-
-"""
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
 from ambari_agent.ClusterCache import ClusterCache
 import logging
 
@@ -85,6 +65,13 @@ class ClusterAlertDefinitionsCache(ClusterCache):
           mutable_dict[cluster_id]['alertDefinitions'].append(alert_definition)
         else:
           mutable_dict[cluster_id]['alertDefinitions'][index_of_alert] = 
alert_definition
+          
+      # for other non-definitions properties
+      for property, value in cache_update[cluster_id].iteritems():
+        if property == 'alertDefinitions':
+          continue
+        
+        mutable_dict[cluster_id][property] = value
 
     self.rewrite_cache(mutable_dict, cache_hash)
 
diff --git a/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py 
b/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
index 2d9e5cc..d559fac 100644
--- a/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
+++ b/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
@@ -75,6 +75,7 @@ class HeartbeatThread(threading.Thread):
     ]
     self.responseId = 0
     self.file_cache = initializer_module.file_cache
+    self.stale_alerts_monitor = initializer_module.stale_alerts_monitor
 
 
   def run(self):
@@ -191,8 +192,21 @@ class HeartbeatThread(threading.Thread):
   def get_heartbeat_body(self):
     """
     Heartbeat body to be send to server
+
+    Heartbeat should as lightweight as possible.
+    It purposes only connectivity and health check (whether all threads are 
ran correctly aka. stale_alerts, stale_component_status).
+    Putting anything in heartbeat poses a problem for big clusters (e.g. 5K 
nodes) as heartbeats are sent often.
+
+    Please use other event threads to send information.
     """
-    return {'id':self.responseId}
+    body = {'id':self.responseId}
+
+    stale_alerts = self.stale_alerts_monitor.get_stale_alerts()
+    if stale_alerts:
+      body['staleAlerts'] = stale_alerts
+
+    return body
+
 
   def establish_connection(self):
     """
diff --git a/ambari-agent/src/main/python/ambari_agent/InitializerModule.py 
b/ambari-agent/src/main/python/ambari_agent/InitializerModule.py
index 144b780..ea2e043 100644
--- a/ambari-agent/src/main/python/ambari_agent/InitializerModule.py
+++ b/ambari-agent/src/main/python/ambari_agent/InitializerModule.py
@@ -34,6 +34,7 @@ from ambari_agent.CustomServiceOrchestrator import 
CustomServiceOrchestrator
 from ambari_agent.RecoveryManager import RecoveryManager
 from ambari_agent.AlertSchedulerHandler import AlertSchedulerHandler
 from ambari_agent.ConfigurationBuilder import ConfigurationBuilder
+from ambari_agent.StaleAlertsMonitor import StaleAlertsMonitor
 from ambari_stomp.adapter.websocket import ConnectionIsAlreadyClosed
 
 logger = logging.getLogger(__name__)
@@ -63,6 +64,7 @@ class InitializerModule:
     self.configurations_cache = 
ClusterConfigurationCache(self.config.cluster_cache_dir)
     self.alert_definitions_cache = 
ClusterAlertDefinitionsCache(self.config.cluster_cache_dir)
     self.configuration_builder = ConfigurationBuilder(self)
+    self.stale_alerts_monitor = StaleAlertsMonitor(self)
 
     self.file_cache = FileCache(self.config)
 
diff --git a/ambari-agent/src/main/python/ambari_agent/StaleAlertsMonitor.py 
b/ambari-agent/src/main/python/ambari_agent/StaleAlertsMonitor.py
new file mode 100644
index 0000000..b2053d3
--- /dev/null
+++ b/ambari-agent/src/main/python/ambari_agent/StaleAlertsMonitor.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import logging
+
+logger = logging.getLogger(__name__)
+
+class StaleAlertsMonitor():
+  """
+  For tracks which alerts were not ran or collected for a long time, to report 
the data to server.
+  Which is shown as a separate alert on server.
+  """
+  def __init__(self, initializer_module):
+    self.alert_definitions_cache = initializer_module.alert_definitions_cache
+    #self.stale_interval_multiplier = 2
+    self.alerts_run_time = {}
+    self.alert_created_time = {}
+
+  def save_executed_alerts(self, alerts):
+    """
+    Saves the last ran time for all the alerts passed
+    """
+    for alert in alerts:
+      timestamp = alert['timestamp'] / 1000
+      alert_id = alert['definitionId']
+      self.alerts_run_time[alert_id] = timestamp
+
+  def get_stale_alerts(self):
+    """
+    Gets alerts which were not ran/collected for 
stale_interval_multiplier*alert_interval time
+    """
+    stale_alert_ids = []
+    curr_time = time.time()
+
+    if not self.alert_definitions_cache:
+      return []
+
+    for cluster_id, command in self.alert_definitions_cache.iteritems():
+      # the cluster has not yet initialized staleAlertsAlert
+      if not 'staleIntervalMultiplier' in command:
+        continue
+
+      stale_interval_multiplier = command['staleIntervalMultiplier']
+      for definition in command['alertDefinitions']:
+        definition_id = definition['definitionId']
+        if not definition_id in self.alerts_run_time:
+          self.alerts_run_time[definition_id] = curr_time
+
+        interval_seconds = definition['interval']*60
+
+        if curr_time > 
self.alerts_run_time[definition_id]+(interval_seconds*stale_interval_multiplier):
+          logger.info("Alert %s got stale. Reporting to the server.", 
definition['name'])
+          last_run_time_ms = int(self.alerts_run_time[definition_id]*1000)
+          stale_alert_ids.append({"id": definition_id, "timestamp": 
last_run_time_ms})
+
+    return stale_alert_ids
\ No newline at end of file
diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py 
b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
index 1414481..05631ce 100644
--- a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
+++ b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
@@ -58,6 +58,11 @@ class BaseAlert(object):
       interval = self.alert_meta['interval']
       return 1 if interval < 1 else interval
 
+  def get_definition_id(self):
+    """
+    gets the id of definition (a number)
+    """
+    return self.alert_meta['definitionId']
 
   def is_enabled(self):
     """
@@ -151,6 +156,7 @@ class BaseAlert(object):
     data['name'] = self._get_alert_meta_value_safely('name')
     data['clusterId'] = self.cluster_id
     data['timestamp'] = long(time.time() * 1000)
+    data['definitionId'] = self.get_definition_id()
 
     try:
       data['state'] = res[0]
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeat.java 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeat.java
index 8cac669..4150d29 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeat.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeat.java
@@ -36,6 +36,9 @@ public class HeartBeat {
   @com.fasterxml.jackson.annotation.JsonProperty("id")
   private long responseId = -1;
 
+  @com.fasterxml.jackson.annotation.JsonProperty("staleAlerts")
+  private List<StaleAlert> staleAlerts = new ArrayList<>();
+
   private long timestamp;
   private String hostname;
   List<CommandReport> reports = new ArrayList<>();
@@ -157,6 +160,10 @@ public class HeartBeat {
     this.alerts = alerts;
   }
 
+  public List<StaleAlert> getStaleAlerts() {
+    return staleAlerts;
+  }
+
   @Override
   public String toString() {
     return "HeartBeat{" +
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index 997d716..3b739df 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -17,11 +17,9 @@
  */
 package org.apache.ambari.server.agent;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 
@@ -31,12 +29,8 @@ import org.apache.ambari.server.actionmanager.ActionManager;
 import org.apache.ambari.server.agent.stomp.dto.HostStatusReport;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
-import org.apache.ambari.server.events.AlertReceivedEvent;
-import org.apache.ambari.server.events.publishers.AlertEventPublisher;
 import org.apache.ambari.server.events.publishers.StateUpdateEventPublisher;
 import org.apache.ambari.server.state.AgentVersion;
-import org.apache.ambari.server.state.Alert;
-import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
@@ -46,13 +40,14 @@ import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.state.alert.AlertDefinition;
 import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.host.HostHealthyHeartbeatEvent;
 import org.apache.ambari.server.state.host.HostRegistrationRequestEvent;
 import org.apache.ambari.server.state.host.HostStatusUpdatesReceivedEvent;
 import org.apache.ambari.server.utils.VersionUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,7 +95,7 @@ public class HeartBeatHandler {
   private AgentSessionManager agentSessionManager;
 
   @Inject
-  private AlertEventPublisher alertEventPublisher;
+  private AlertHelper alertHelper;
 
   private Map<String, Long> hostResponseIds = new ConcurrentHashMap<>();
 
@@ -209,6 +204,10 @@ public class HeartBeatHandler {
       processRecoveryReport(rr, hostname);
     }
 
+    if (CollectionUtils.isNotEmpty(heartbeat.getStaleAlerts())) {
+      alertHelper.addStaleAlerts(hostObject.getHostId(), 
heartbeat.getStaleAlerts());
+    }
+
     try {
       hostObject.handleEvent(new HostHealthyHeartbeatEvent(hostname, now,
           heartbeat.getAgentEnv(), heartbeat.getMounts()));
@@ -225,8 +224,6 @@ public class HeartBeatHandler {
       annotateResponse(hostname, response);
     }
 
-    updateAlertLastStateTimestamp(hostname);
-
     return response;
   }
 
@@ -341,6 +338,8 @@ public class HeartBeatHandler {
     // Save the prefix of the log file paths
     hostObject.setPrefix(register.getPrefix());
 
+    alertHelper.clearStaleAlerts(hostObject.getHostId());
+
     hostObject.handleEvent(new HostRegistrationRequestEvent(hostname,
         null != register.getPublicHostname() ? register.getPublicHostname() : 
hostname,
         new AgentVersion(register.getAgentVersion()), now, 
register.getHardwareProfile(),
@@ -408,40 +407,6 @@ public class HeartBeatHandler {
     return response;
   }
 
-  /**
-   * Heartbeat receiving indicates agent-server connection is in order. This 
mean that all alerts statuses are actual
-   * and last state timestamp should be updated.
-   * @param hostName hostName for heartbeat
-   * @throws AmbariException
-   */
-  private void updateAlertLastStateTimestamp(String hostName) throws 
AmbariException {
-    Set<Cluster> hostClusters = clusterFsm.getClustersForHost(hostName);
-    if (null == hostClusters || hostClusters.size() == 0) {
-      return;
-    }
-
-    List<Alert> alerts = new ArrayList<>();
-    // for every cluster this host is a member of, build the command
-    for (Cluster cluster : hostClusters) {
-      String clusterName = cluster.getClusterName();
-      alertDefinitionHash.invalidate(clusterName, hostName);
-
-      List<AlertDefinition> definitions = 
alertDefinitionHash.getAlertDefinitions(
-          clusterName, hostName);
-      for (AlertDefinition alertDefinition : definitions) {
-        Alert toUpdate = new Alert();
-        toUpdate.setHostName(hostName);
-        toUpdate.setClusterId(alertDefinition.getClusterId());
-        toUpdate.setTimestamp(System.currentTimeMillis());
-        toUpdate.setName(alertDefinition.getName());
-        toUpdate.setState(AlertState.SKIPPED);
-        alerts.add(toUpdate);
-      }
-    }
-    AlertReceivedEvent alertReceivedEvents = new AlertReceivedEvent(alerts);
-    alertEventPublisher.publish(alertReceivedEvents);
-  }
-
   public void stop() {
     heartbeatMonitor.shutdown();
     heartbeatProcessor.stopAsync();
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/StaleAlert.java 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/StaleAlert.java
new file mode 100644
index 0000000..f2f1c01
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/StaleAlert.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.agent;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class StaleAlert {
+
+  /**
+   * Alert definition id
+   */
+  @JsonProperty("id")
+  private Long id;
+
+  /**
+   * Alert stale timestamp
+   */
+  @JsonProperty("timestamp")
+  private Long timestamp;
+
+  public StaleAlert() {
+  }
+
+  public StaleAlert(Long id, Long timestamp) {
+    this.id = id;
+    this.timestamp = timestamp;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public Long getTimestamp() {
+    return timestamp;
+  }
+
+  @Override
+  public String toString() {
+    return "StaleAlert{" +
+        "id=" + id +
+        ", timestamp=" + timestamp +
+        '}';
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
index 1a6f7dc..6bf0587 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
@@ -34,10 +34,15 @@ import 
org.apache.ambari.server.events.AlertDefinitionEventType;
 import org.apache.ambari.server.events.AlertDefinitionsAgentUpdateEvent;
 import org.apache.ambari.server.events.HostsAddedEvent;
 import org.apache.ambari.server.events.HostsRemovedEvent;
+import 
org.apache.ambari.server.events.listeners.alerts.AlertDefinitionsUIUpdateListener;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
 import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,6 +61,15 @@ public class AlertDefinitionsHolder extends 
AgentHostDataHolder<AlertDefinitions
   private Provider<Clusters> clusters;
 
   @Inject
+  private AlertDefinitionDAO alertDefinitionDAO;
+
+  @Inject
+  private AlertHelper alertHelper;
+
+  @Inject
+  private AlertDefinitionFactory alertDefinitionFactory;
+
+  @Inject
   public AlertDefinitionsHolder(AmbariEventPublisher eventPublisher) {
     eventPublisher.register(this);
   }
@@ -69,7 +83,11 @@ public class AlertDefinitionsHolder extends 
AgentHostDataHolder<AlertDefinitions
     for (Map.Entry<Long, Map<Long, AlertDefinition>> e : 
alertDefinitions.entrySet()) {
       Long clusterId = e.getKey();
       Map<Long, AlertDefinition> definitionMap = e.getValue();
-      result.put(clusterId, new AlertCluster(definitionMap, hostName));
+
+      AlertDefinitionEntity ambariStaleAlert = 
alertDefinitionDAO.findByName(clusterId, 
AlertDefinitionsUIUpdateListener.AMBARI_STALE_ALERT_NAME);
+      Integer staleIntervalMultiplier = 
alertHelper.getWaitFactorMultiplier(alertDefinitionFactory.coerce(ambariStaleAlert));
+
+      result.put(clusterId, new AlertCluster(definitionMap, hostName, 
staleIntervalMultiplier));
       count += definitionMap.size();
     }
     LOG.info("Loaded {} alert definitions for {} clusters for host {}", count, 
result.size(), hostName);
@@ -163,4 +181,12 @@ public class AlertDefinitionsHolder extends 
AgentHostDataHolder<AlertDefinitions
     AlertDefinitionsAgentUpdateEvent event = new 
AlertDefinitionsAgentUpdateEvent(eventType, update, hostName, hostId);
     safelyUpdateData(event);
   }
+
+  public void provideStaleAlertDefinitionUpdateEvent(AlertDefinitionEventType 
eventType, Long clusterId,
+                                                     Integer 
staleIntervalMultiplier, String hostName) throws AmbariException {
+    Long hostId = clusters.get().getHost(hostName).getHostId();
+    Map<Long, AlertCluster> update = Collections.singletonMap(clusterId, new 
AlertCluster(Collections.emptyMap(), hostName, staleIntervalMultiplier));
+    AlertDefinitionsAgentUpdateEvent event = new 
AlertDefinitionsAgentUpdateEvent(eventType, update, hostName, hostId);
+    safelyUpdateData(event);
+  }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
index 67f3526..fb92d47 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
@@ -30,21 +30,38 @@ import org.slf4j.LoggerFactory;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
+@JsonInclude(JsonInclude.Include.NON_NULL)
 public class AlertCluster {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(AlertCluster.class);
 
   private final Map<Long, AlertDefinition> alertDefinitions;
   private final String hostName;
+  private Integer staleIntervalMultiplier;
+
+  public AlertCluster(AlertDefinition alertDefinition, String hostName, 
Integer staleIntervalMultiplier) {
+    this(Collections.singletonMap(alertDefinition.getDefinitionId(), 
alertDefinition), hostName, staleIntervalMultiplier);
+  }
+
+  public AlertCluster(Map<Long, AlertDefinition> alertDefinitions, String 
hostName, Integer staleIntervalMultiplier) {
+    this.alertDefinitions = new HashMap<>(alertDefinitions);
+    this.hostName = hostName;
+    this.staleIntervalMultiplier = staleIntervalMultiplier;
+  }
 
   public AlertCluster(AlertDefinition alertDefinition, String hostName) {
-    this(Collections.singletonMap(alertDefinition.getDefinitionId(), 
alertDefinition), hostName);
+    this(Collections.singletonMap(alertDefinition.getDefinitionId(), 
alertDefinition), hostName, null);
   }
 
   public AlertCluster(Map<Long, AlertDefinition> alertDefinitions, String 
hostName) {
     this.alertDefinitions = new HashMap<>(alertDefinitions);
     this.hostName = hostName;
+    this.staleIntervalMultiplier = null;
+  }
+
+  @JsonProperty("staleIntervalMultiplier")
+  public Integer getStaleIntervalMultiplier() {
+    return staleIntervalMultiplier;
   }
 
   @JsonProperty("alertDefinitions")
@@ -58,10 +75,6 @@ public class AlertCluster {
   }
 
   public boolean handleUpdate(AlertDefinitionEventType eventType, AlertCluster 
update) {
-    if (update.alertDefinitions.isEmpty()) {
-      return false;
-    }
-
     boolean changed = false;
 
     switch (eventType) {
@@ -79,6 +92,11 @@ public class AlertCluster {
             changed = changed || !oldDefinition.deeplyEquals(newDefinition);
           }
         }
+        if (update.getStaleIntervalMultiplier() != null
+            && 
!update.getStaleIntervalMultiplier().equals(staleIntervalMultiplier)) {
+          staleIntervalMultiplier = update.getStaleIntervalMultiplier();
+          changed = true;
+        }
         LOG.debug("Handled {} of {} alerts, changed = {}", eventType, 
update.alertDefinitions.size(), changed);
         break;
       case DELETE:
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/AlertRunnable.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/AlertRunnable.java
index bdcffbc..c947054 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/AlertRunnable.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/AlertRunnable.java
@@ -30,7 +30,7 @@ import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
-import org.apache.commons.lang.math.NumberUtils;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -76,6 +76,9 @@ public abstract class AlertRunnable implements Runnable {
   @Inject
   private AlertEventPublisher m_alertEventPublisher;
 
+  @Inject
+  protected AlertHelper alertHelper;
+
   /**
    * Constructor.
    *
@@ -137,34 +140,6 @@ public abstract class AlertRunnable implements Runnable {
   }
 
   /**
-   * Converts the given value to an integer safely.
-   *
-   * @param value
-   * @param defaultValue
-   * @return
-   */
-  int getThresholdValue(Object value, int defaultValue) {
-    if (null == value) {
-      return defaultValue;
-    }
-
-    if (value instanceof Number) {
-      return ((Number) value).intValue();
-    }
-
-    if (!(value instanceof String)) {
-      value = value.toString();
-    }
-
-    if (!NumberUtils.isNumber((String) value)) {
-      return defaultValue;
-    }
-
-    Number number = NumberUtils.createNumber((String) value);
-    return number.intValue();
-  }
-
-  /**
    * Builds an {@link Alert} instance.
    *
    * @param cluster
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnable.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnable.java
index b30e689..d6accba 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnable.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnable.java
@@ -371,11 +371,11 @@ public class AmbariPerformanceRunnable extends 
AlertRunnable {
       Object value = parameter.getValue();
 
       if (StringUtils.equals(parameter.getName(), area.m_warningParameter)) {
-        warningThreshold = getThresholdValue(value, warningThreshold);
+        warningThreshold = alertHelper.getThresholdValue(value, 
warningThreshold);
       }
 
       if (StringUtils.equals(parameter.getName(), area.m_criticalParameter)) {
-        criticalThreshold = getThresholdValue(value, criticalThreshold);
+        criticalThreshold = alertHelper.getThresholdValue(value, 
criticalThreshold);
       }
     }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/StaleAlertRunnable.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/StaleAlertRunnable.java
index 036381e..f71ebb7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/alerts/StaleAlertRunnable.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/alerts/StaleAlertRunnable.java
@@ -20,13 +20,16 @@ package org.apache.ambari.server.alerts;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.text.MessageFormat;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
+import org.apache.ambari.server.agent.stomp.AlertDefinitionsHolder;
 import org.apache.ambari.server.orm.dao.AlertsDAO;
 import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
@@ -34,11 +37,11 @@ import 
org.apache.ambari.server.orm.entities.AlertHistoryEntity;
 import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.alert.AlertDefinition;
 import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
-import org.apache.ambari.server.state.alert.ParameterizedSource.AlertParameter;
-import org.apache.ambari.server.state.alert.ServerSource;
 import org.apache.ambari.server.state.alert.SourceType;
 import org.apache.ambari.server.state.services.AmbariServerAlertService;
 import org.apache.commons.lang.StringUtils;
@@ -84,19 +87,6 @@ public class StaleAlertRunnable extends AlertRunnable {
   private static final int MINUTES_PER_DAY = 24 * 60;
   private static final int MINUTES_PER_HOUR = 60;
 
-  /**
-   * The multiplier for the interval of the definition which is being checked
-   * for staleness. If this value is {@code 2}, then alerts are considered 
stale
-   * if they haven't run in more than 2x their interval value.
-   */
-  private static final int INTERVAL_WAIT_FACTOR_DEFAULT = 2;
-
-  /**
-   * A parameter which exposes the interval multipler to use for calculating
-   * staleness. If this does not exist, then
-   * {@link #INTERVAL_WAIT_FACTOR_DEFAULT} will be used.
-   */
-  private static final String STALE_INTERVAL_MULTIPLIER_PARAM_KEY = 
"stale.interval.multiplier";
 
   /**
    * Used to get the current alerts and the last time they ran.
@@ -111,6 +101,9 @@ public class StaleAlertRunnable extends AlertRunnable {
   @Inject
   private AlertDefinitionFactory m_definitionFactory;
 
+  @Inject
+  private AlertDefinitionsHolder alertDefinitionsHolder;
+
   /**
    * Constructor.
    *
@@ -126,7 +119,8 @@ public class StaleAlertRunnable extends AlertRunnable {
   @Override
   List<Alert> execute(Cluster cluster, AlertDefinitionEntity myDefinition) {
     // get the multiplier
-    int waitFactor = getWaitFactorMultiplier(myDefinition);
+    AlertDefinition alertDefinition = m_definitionFactory.coerce(myDefinition);
+    int waitFactor = alertHelper.getWaitFactorMultiplier(alertDefinition);
 
     // use the uptime of the Ambari Server as a way to determine if we need to
     // give the alert more time to report in
@@ -144,6 +138,8 @@ public class StaleAlertRunnable extends AlertRunnable {
 
     long now = System.currentTimeMillis();
 
+    Map<Long, List<Long>> alertDefinitionsToHosts = 
prepareHostDefinitions(cluster.getClusterId());
+
     // for each current alert, check to see if the last time it ran is
     // more than INTERVAL_WAIT_FACTOR * its interval value (indicating it 
hasn't
     // run)
@@ -186,10 +182,45 @@ public class StaleAlertRunnable extends AlertRunnable {
         continue;
       }
 
-      // if the last time it was run is >= INTERVAL_WAIT_FACTOR * the interval,
-      // it's stale
-      long timeDifference = now - current.getLatestTimestamp();
-      if (timeDifference >= waitFactor * intervalInMillis) {
+      Boolean timedout;
+      Long lastCheckTimestamp = 0L;
+
+      // alert history for component or ambari agent should contains the 
hostname
+      // host/hosts for alerts for master component/with host ignoring can be 
retrieved from current agent's alert definitions
+
+      String currentHostName = history.getHostName();
+      List<Host> hosts = new ArrayList<>();
+      if (currentHostName != null) {
+        hosts.add(cluster.getHost(currentHostName));
+      } else if 
(alertDefinitionsToHosts.containsKey(current.getDefinitionId())) {
+        hosts = alertDefinitionsToHosts.get(current.getDefinitionId()).stream()
+            .map(i -> cluster.getHost(i)).collect(Collectors.toList());
+      }
+      if (!hosts.isEmpty()) {
+
+        // in case alert ignores host we should to check alert is stale on all 
hosts
+        timedout = true;
+        for (Host host : hosts) {
+          if (timedout) {
+            // check agent reported about stale alert
+            if 
(alertHelper.getStaleAlerts(host.getHostId()).containsKey(current.getDefinitionId()))
 {
+              lastCheckTimestamp = Math.max(lastCheckTimestamp,
+                  
alertHelper.getStaleAlerts(host.getHostId()).get(current.getDefinitionId()));
+            // check host is in HEARTBEAT_LOST state
+            } else if (host.getState().equals(HostState.HEARTBEAT_LOST)) {
+              lastCheckTimestamp = Math.max(lastCheckTimestamp, 
host.getLastHeartbeatTime());
+            } else {
+              timedout = false;
+            }
+          }
+        }
+      } else {
+        // Server alerts will be checked by the old way
+        long timeDifference = now - current.getLatestTimestamp();
+        timedout = timeDifference >= waitFactor * intervalInMillis;
+        lastCheckTimestamp = current.getOriginalTimestamp();
+      }
+      if (timedout) {
         // increase the count
         totalStaleAlerts++;
 
@@ -208,6 +239,7 @@ public class StaleAlertRunnable extends AlertRunnable {
             staleAlertsByHost.put(hostName, new TreeSet<>());
           }
 
+          long timeDifference = now - lastCheckTimestamp;
           
staleAlertsByHost.get(hostName).add(MessageFormat.format(TIMED_LABEL_MSG, label,
               millisToHumanReadableStr(timeDifference)));
         } else {
@@ -245,6 +277,25 @@ public class StaleAlertRunnable extends AlertRunnable {
   }
 
   /**
+   * Retrieves alert definitions sent to agents.
+   * @param clusterId cluster id
+   * @return map definition id - host ids list
+   */
+  public Map<Long, List<Long>> prepareHostDefinitions(Long clusterId) {
+    Map<Long, List<Long>> alertDefinitionsToHosts = new HashMap<>();
+
+    alertDefinitionsHolder.getData().entrySet().stream()
+        .filter(e -> e.getValue().getClusters() != null)
+        .filter(e -> e.getValue().getClusters().get(clusterId) != null)
+        .forEach(e -> 
e.getValue().getClusters().get(clusterId).getAlertDefinitions().stream()
+            .forEach(l -> {
+              alertDefinitionsToHosts.putIfAbsent(l.getDefinitionId(), new 
ArrayList<>());
+              alertDefinitionsToHosts.get(l.getDefinitionId()).add(e.getKey());
+            }));
+    return alertDefinitionsToHosts;
+  }
+
+  /**
    * Converts given {@code milliseconds} to human-readable {@link String} like 
"1d 2h 3m" or "2h 4m".
    * @param milliseconds milliseconds to convert
    * @return human-readable string
@@ -268,49 +319,5 @@ public class StaleAlertRunnable extends AlertRunnable {
     }
     return result.trim();
   }
-
-  /**
-   * Gets the wait factor multiplier off of the definition, returning
-   * {@link #INTERVAL_WAIT_FACTOR_DEFAULT} if not specified. This will look for
-   * {@link #STALE_INTERVAL_MULTIPLIER_PARAM_KEY} in the definition parameters.
-   * The value returned from this method will be guaranteed to be in the range
-   * of 2 to 10.
-   *
-   * @param entity
-   *          the definition to read
-   * @return the wait factor interval multiplier
-   */
-  private int getWaitFactorMultiplier(AlertDefinitionEntity entity) {
-    // start with the default
-    int waitFactor = INTERVAL_WAIT_FACTOR_DEFAULT;
-
-    // coerce the entity into a business object so that the list of parameters
-    // can be extracted and used for threshold calculation
-    try {
-      AlertDefinition definition = m_definitionFactory.coerce(entity);
-      ServerSource serverSource = (ServerSource) definition.getSource();
-      List<AlertParameter> parameters = serverSource.getParameters();
-      for (AlertParameter parameter : parameters) {
-        Object value = parameter.getValue();
-
-        if (StringUtils.equals(parameter.getName(), 
STALE_INTERVAL_MULTIPLIER_PARAM_KEY)) {
-          waitFactor = getThresholdValue(value, INTERVAL_WAIT_FACTOR_DEFAULT);
-        }
-      }
-
-      if (waitFactor < 2 || waitFactor > 10) {
-        LOG.warn(
-            "The interval multipler of {} is outside the valid range for {} 
and will be set to 2",
-            waitFactor, entity.getLabel());
-
-        waitFactor = 2;
-      }
-    } catch (Exception exception) {
-      LOG.error("Unable to read the {} parameter for {}", 
STALE_INTERVAL_MULTIPLIER_PARAM_KEY,
-          StaleAlertRunnable.class.getSimpleName(), exception);
-    }
-
-    return waitFactor;
-  }
 }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
index 0f0c7b2..dd1d6e0 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertResourceProvider.java
@@ -68,6 +68,7 @@ public class AlertResourceProvider extends 
ReadOnlyResourceProvider implements
   public static final String ALERT_ID = "Alert/id";
   public static final String ALERT_STATE = "Alert/state";
   public static final String ALERT_ORIGINAL_TIMESTAMP = 
"Alert/original_timestamp";
+  // TODO remove after UI-side fix
   public static final String ALERT_LATEST_TIMESTAMP = "Alert/latest_timestamp";
   public static final String ALERT_MAINTENANCE_STATE = 
"Alert/maintenance_state";
   public static final String ALERT_DEFINITION_ID = "Alert/definition_id";
@@ -118,6 +119,7 @@ public class AlertResourceProvider extends 
ReadOnlyResourceProvider implements
     PROPERTY_IDS.add(ALERT_DEFINITION_ID);
     PROPERTY_IDS.add(ALERT_DEFINITION_NAME);
     PROPERTY_IDS.add(ALERT_CLUSTER_NAME);
+    // TODO remove after UI-side fix
     PROPERTY_IDS.add(ALERT_LATEST_TIMESTAMP);
     PROPERTY_IDS.add(ALERT_MAINTENANCE_STATE);
     PROPERTY_IDS.add(ALERT_INSTANCE);
@@ -260,6 +262,7 @@ public class AlertResourceProvider extends 
ReadOnlyResourceProvider implements
     Resource resource = new ResourceImpl(Resource.Type.Alert);
     setResourceProperty(resource, ALERT_CLUSTER_NAME, clusterName, 
requestedIds);
     setResourceProperty(resource, ALERT_ID, entity.getAlertId(), requestedIds);
+    // TODO remove after UI-side fix
     setResourceProperty(resource, ALERT_LATEST_TIMESTAMP, 
entity.getLatestTimestamp(), requestedIds);
     setResourceProperty(resource, ALERT_MAINTENANCE_STATE, 
entity.getMaintenanceState(), requestedIds);
     setResourceProperty(resource, ALERT_ORIGINAL_TIMESTAMP, 
entity.getOriginalTimestamp(), requestedIds);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertDefinitionsUIUpdateListener.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertDefinitionsUIUpdateListener.java
index 379ec4c..e4f9cf9 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertDefinitionsUIUpdateListener.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertDefinitionsUIUpdateListener.java
@@ -41,8 +41,10 @@ import 
org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.StateUpdateEventPublisher;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.alert.AlertDefinition;
 import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,10 +71,15 @@ public class AlertDefinitionsUIUpdateListener {
   private AlertDefinitionsHolder alertDefinitionsHolder;
 
   @Inject
+  private AlertHelper alertHelper;
+
+  @Inject
   public AlertDefinitionsUIUpdateListener(AmbariEventPublisher 
ambariEventPublisher) {
     ambariEventPublisher.register(this);
   }
 
+  public final static String AMBARI_STALE_ALERT_NAME = 
"ambari_server_stale_alerts";
+
   @Subscribe
   public void onAlertDefinitionRegistered(AlertDefinitionRegistrationEvent 
event) throws AmbariException {
     handleSingleDefinitionChange(UPDATE, event.getDefinition());
@@ -132,6 +139,12 @@ public class AlertDefinitionsUIUpdateListener {
       alertDefinitionsHolder.provideAlertDefinitionAgentUpdateEvent(eventType, 
alertDefinition.getClusterId(),
           Collections.singletonMap(alertDefinition.getDefinitionId(), 
alertDefinition), hostName);
     }
+    if (alertDefinition.getName().equals(AMBARI_STALE_ALERT_NAME)) {
+      for (Host host : 
clusters.get().getCluster(alertDefinition.getClusterId()).getHosts()) {
+        
alertDefinitionsHolder.provideStaleAlertDefinitionUpdateEvent(eventType, 
alertDefinition.getClusterId(),
+            alertHelper.getWaitFactorMultiplier(alertDefinition), 
host.getHostName());
+      }
+    }
     Map<Long, AlertCluster> update = 
Collections.singletonMap(alertDefinition.getClusterId(), new 
AlertCluster(alertDefinition, null));
     AlertDefinitionsUIUpdateEvent event = new 
AlertDefinitionsUIUpdateEvent(eventType, update);
     stateUpdateEventPublisher.publish(event);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
index 5aba046..9a8f66d 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
@@ -50,10 +50,13 @@ import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.apache.ambari.server.state.alert.SourceType;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.math.NumberUtils;
+import org.eclipse.jetty.util.StringUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -106,6 +109,9 @@ public class AlertReceivedListener {
   @Inject
   private Provider<MaintenanceStateHelper> m_maintenanceStateHelper;
 
+  @Inject
+  private AlertHelper alertHelper;
+
   /**
    * Receives and publishes {@link AlertEvent} instances.
    */
@@ -137,7 +143,7 @@ public class AlertReceivedListener {
   @Subscribe
   @AllowConcurrentEvents
   @RequiresSession
-  public void onAlertEvent(AlertReceivedEvent event) {
+  public void onAlertEvent(AlertReceivedEvent event) throws AmbariException {
     if (LOG.isDebugEnabled()) {
       LOG.debug(event.toString());
     }
@@ -239,6 +245,7 @@ public class AlertReceivedListener {
           current.setAlertHistory(history);
           current.setLatestTimestamp(alert.getTimestamp());
           current.setOriginalTimestamp(alert.getTimestamp());
+          clearStaleAlerts(alert.getHostName(), definition.getDefinitionId());
 
           // brand new alert instances being received are always HARD
           current.setFirmness(AlertFirmness.HARD);
@@ -256,6 +263,7 @@ public class AlertReceivedListener {
 
         // update the timestamp no matter what
         current.setLatestTimestamp(alert.getTimestamp());
+        clearStaleAlerts(alert.getHostName(), definition.getDefinitionId());
 
         // only update some fields if the alert isn't SKIPPED
         if (alertState != AlertState.SKIPPED) {
@@ -315,6 +323,8 @@ public class AlertReceivedListener {
         current.setOriginalTimestamp(alert.getTimestamp());
         current.setLatestText(alert.getText());
 
+        clearStaleAlerts(alert.getHostName(), definition.getDefinitionId());
+
         current.setAlertHistory(history);
 
         // figure out how to set the occurrences correctly
@@ -378,6 +388,18 @@ public class AlertReceivedListener {
     }
   }
 
+  private void clearStaleAlerts(String hostName, Long definitionId) throws 
AmbariException {
+    if (StringUtil.isNotBlank(hostName)) {
+      Host host = m_clusters.get().getHosts().stream().filter(h -> 
h.getHostName().equals(hostName))
+          .findFirst().orElse(null);
+      if (host != null) {
+        alertHelper.clearStaleAlert(host.getHostId(), definitionId);
+      }
+    } else {
+      alertHelper.clearStaleAlert(definitionId);
+    }
+  }
+
   private void updateAlertDetails(Alert alert, AlertDefinitionEntity 
definition) {
     if (alert.getService() == null) {
       alert.setService(definition.getServiceName());
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index d433743..58233fd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -147,6 +147,14 @@ public interface Cluster {
    */
   Host getHost(String hostName);
 
+  /**
+   * Get specific host info using host id.
+   *
+   * @param hostId the host id
+   * @return Host info {@link Host}
+   */
+  Host getHost(Long hostId);
+
 
   /**
    * Adds schs to cluster AND persists them
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertHelper.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertHelper.java
new file mode 100644
index 0000000..81e9ac3
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertHelper.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.state.alert;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+import org.apache.ambari.server.agent.StaleAlert;
+import org.apache.ambari.server.alerts.StaleAlertRunnable;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Singleton;
+
+@Singleton
+public class AlertHelper {
+  /**
+   * Logger.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(AlertHelper.class);
+
+  /**
+   * The multiplier for the interval of the definition which is being checked
+   * for staleness. If this value is {@code 2}, then alerts are considered 
stale
+   * if they haven't run in more than 2x their interval value.
+   */
+  private static final int INTERVAL_WAIT_FACTOR_DEFAULT = 2;
+
+  /**
+   * A parameter which exposes the interval multipler to use for calculating
+   * staleness. If this does not exist, then
+   * {@link #INTERVAL_WAIT_FACTOR_DEFAULT} will be used.
+   */
+  private static final String STALE_INTERVAL_MULTIPLIER_PARAM_KEY = 
"stale.interval.multiplier";
+
+  /**
+   * Contains stale alerts with stale timestamp by host id.
+   */
+  private ConcurrentMap<Long, ConcurrentMap<Long, Long>> staleAlerts = new 
ConcurrentHashMap<>();
+
+  /**
+   * Gets the wait factor multiplier off of the definition, returning
+   * {@link #INTERVAL_WAIT_FACTOR_DEFAULT} if not specified. This will look for
+   * {@link #STALE_INTERVAL_MULTIPLIER_PARAM_KEY} in the definition parameters.
+   * The value returned from this method will be guaranteed to be in the range
+   * of 2 to 10.
+   *
+   * @param definition
+   *          the definition to read
+   * @return the wait factor interval multiplier
+   */
+  public int getWaitFactorMultiplier(AlertDefinition definition) {
+    // start with the default
+    int waitFactor = INTERVAL_WAIT_FACTOR_DEFAULT;
+
+    // coerce the entity into a business object so that the list of parameters
+    // can be extracted and used for threshold calculation
+    try {
+      ServerSource serverSource = (ServerSource) definition.getSource();
+      List<ParameterizedSource.AlertParameter> parameters = 
serverSource.getParameters();
+      for (ParameterizedSource.AlertParameter parameter : parameters) {
+        Object value = parameter.getValue();
+
+        if (StringUtils.equals(parameter.getName(), 
STALE_INTERVAL_MULTIPLIER_PARAM_KEY)) {
+          waitFactor = getThresholdValue(value, INTERVAL_WAIT_FACTOR_DEFAULT);
+        }
+      }
+
+      if (waitFactor < 2 || waitFactor > 10) {
+        LOG.warn(
+            "The interval multipler of {} is outside the valid range for {} 
and will be set to 2",
+            waitFactor, definition.getLabel());
+
+        waitFactor = 2;
+      }
+    } catch (Exception exception) {
+      LOG.error("Unable to read the {} parameter for {}", 
STALE_INTERVAL_MULTIPLIER_PARAM_KEY,
+          StaleAlertRunnable.class.getSimpleName(), exception);
+    }
+
+    return waitFactor;
+  }
+
+  /**
+   * Converts the given value to an integer safely.
+   *
+   * @param value
+   * @param defaultValue
+   * @return
+   */
+  public int getThresholdValue(Object value, int defaultValue) {
+    if (null == value) {
+      return defaultValue;
+    }
+
+    if (value instanceof Number) {
+      return ((Number) value).intValue();
+    }
+
+    if (!(value instanceof String)) {
+      value = value.toString();
+    }
+
+    if (!NumberUtils.isNumber((String) value)) {
+      return defaultValue;
+    }
+
+    Number number = NumberUtils.createNumber((String) value);
+    return number.intValue();
+  }
+
+  /**
+   * Saves stale alerts for specified host.
+   * @param hostId host id
+   * @param staleAlertsDefinitionId list of stale alerts
+   */
+  public void addStaleAlerts(Long hostId, List<StaleAlert> 
staleAlertsDefinitionId) {
+    staleAlerts.putIfAbsent(hostId, new ConcurrentHashMap<>());
+    ConcurrentMap<Long, Long> hostStaleAlerts = staleAlerts.get(hostId);
+    staleAlertsDefinitionId.forEach(s -> hostStaleAlerts.put(s.getId(), 
s.getTimestamp()));
+  }
+
+  /**
+   * Retrieves stale alerts for specified host.
+   * @param hostId host id
+   * @return
+   */
+  public Map<Long, Long> getStaleAlerts(Long hostId) {
+    return staleAlerts.containsKey(hostId) ? new 
HashMap<>(staleAlerts.get(hostId)) : Collections.emptyMap();
+  }
+
+  /**
+   * Removes all stale alerts for specified host.
+   * @param hostId host id
+   */
+  public void clearStaleAlerts(Long hostId) {
+    staleAlerts.remove(hostId);
+  }
+
+  /**
+   * Removes specified stale alert from specified host.
+   * @param hostId host id.
+   * @param definitionId alert definition id.
+   */
+  public void clearStaleAlert(Long hostId, Long definitionId) {
+    if (staleAlerts.containsKey(hostId)) {
+      staleAlerts.get(hostId).remove(definitionId);
+    }
+  }
+
+  /**
+   * Removes specified stale alert from specified host.
+   * @param definitionId alert definition id.
+   */
+  public void clearStaleAlert(Long definitionId) {
+    staleAlerts.forEach((k, v) -> v.remove(definitionId));
+  }
+
+  /**
+   * Retrieves all host ids containing specified definition id.
+   * @param definitionId alert definition id.
+   * @return host ids list.
+   */
+  public List<Long> getHostIdsByDefinitionId(Long definitionId) {
+    return staleAlerts.entrySet().stream()
+        .filter(e -> e.getValue().containsKey(definitionId))
+        .map(e -> e.getKey()).collect(Collectors.toList());
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 842f55f..bf4ff8d 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -2213,6 +2213,23 @@ List<ClusterConfigEntity> appliedConfigs = new 
ArrayList<>();    String serviceN
   }
 
   @Override
+  public Host getHost(final Long hostId) {
+    if (hostId == null) {
+      return null;
+    }
+
+    Collection<Host> hosts = getHosts();
+    if(hosts != null) {
+      for (Host host : hosts) {
+        if(hostId.equals(host.getHostId())) {
+          return host;
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
   public Collection<Host> getHosts() {
     Map<String, Host> hosts;
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/alerts/AlertHelperTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/AlertHelperTest.java
new file mode 100644
index 0000000..aa4d90f
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/AlertHelperTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.alerts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ambari.server.agent.StaleAlert;
+import org.apache.ambari.server.state.alert.AlertHelper;
+import org.junit.Test;
+
+public class AlertHelperTest {
+
+  @Test
+  public void testThresholdCalculations() {
+    AlertHelper alertHelper = new AlertHelper();
+
+    assertEquals(1, alertHelper.getThresholdValue(1, 2));
+    assertEquals(1, alertHelper.getThresholdValue("1", 2));
+    assertEquals(1, alertHelper.getThresholdValue("1.00", 2));
+    assertEquals(1, alertHelper.getThresholdValue("foo", 1));
+    assertEquals(1, alertHelper.getThresholdValue(new Object(), 1));
+  }
+
+  @Test
+  public void testStaleAlertsOperations() {
+    AlertHelper alertHelper = new AlertHelper();
+
+    alertHelper.addStaleAlerts(1L, new ArrayList<StaleAlert>(){{
+      add(new StaleAlert(1L, 111L));
+      add(new StaleAlert(2L, 111L));
+    }});
+    alertHelper.addStaleAlerts(1L, new ArrayList<StaleAlert>(){{
+      add(new StaleAlert(3L, 111L));
+      add(new StaleAlert(4L, 111L));
+    }});
+
+    assertEquals(4, alertHelper.getStaleAlerts(1L).size());
+
+
+    alertHelper.addStaleAlerts(2L, new ArrayList<StaleAlert>(){{
+      add(new StaleAlert(1L, 111L));
+      add(new StaleAlert(2L, 111L));
+    }});
+
+    List<Long> hostIds = alertHelper.getHostIdsByDefinitionId(1L);
+    assertEquals(2, hostIds.size());
+    assertTrue(hostIds.contains(1L));
+    assertTrue(hostIds.contains(2L));
+  }
+}
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnableTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnableTest.java
index 8f74548..8ffeec7 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnableTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/AmbariPerformanceRunnableTest.java
@@ -248,22 +248,6 @@ public class AmbariPerformanceRunnableTest {
   }
 
   /**
-   * Tests that the event is triggerd with a status of UNKNOWN.
-   */
-  @Test
-  public void testThresholdCalculations() {
-    // instantiate and inject mocks
-    AmbariPerformanceRunnable runnable = new AmbariPerformanceRunnable(
-        m_definition.getDefinitionName());
-
-    assertEquals(1, runnable.getThresholdValue(1, 2));
-    assertEquals(1, runnable.getThresholdValue("1", 2));
-    assertEquals(1, runnable.getThresholdValue("1.00", 2));
-    assertEquals(1, runnable.getThresholdValue("foo", 1));
-    assertEquals(1, runnable.getThresholdValue(new Object(), 1));
-  }
-
-  /**
    *
    */
   private class MockModule implements Module {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/alerts/StaleAlertRunnableTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/StaleAlertRunnableTest.java
index 915eaed..bcc7f05 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/alerts/StaleAlertRunnableTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/alerts/StaleAlertRunnableTest.java
@@ -19,7 +19,13 @@
 package org.apache.ambari.server.alerts;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyString;
 import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.reset;
@@ -29,10 +35,15 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.ambari.server.agent.stomp.AlertDefinitionsHolder;
+import org.apache.ambari.server.agent.stomp.dto.AlertCluster;
+import org.apache.ambari.server.events.AlertDefinitionEventType;
+import org.apache.ambari.server.events.AlertDefinitionsAgentUpdateEvent;
 import org.apache.ambari.server.events.AlertEvent;
 import org.apache.ambari.server.events.AlertReceivedEvent;
 import org.apache.ambari.server.events.MockEventListener;
@@ -47,7 +58,11 @@ import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.apache.ambari.server.state.alert.AlertHelper;
 import org.apache.ambari.server.testutils.PartialNiceMockBinder;
 import org.easymock.EasyMock;
 import org.junit.After;
@@ -88,6 +103,8 @@ public class StaleAlertRunnableTest {
   private AlertDefinitionEntity m_definition;
   private List<AlertCurrentEntity> m_currentAlerts = new ArrayList<>();
   private MockEventListener m_listener;
+  private AlertHelper m_alertHelper;
+  private Host m_host;
 
   private AlertEventPublisher m_eventPublisher;
   private EventBus m_synchronizedBus;
@@ -106,6 +123,7 @@ public class StaleAlertRunnableTest {
     m_eventPublisher = m_injector.getInstance(AlertEventPublisher.class);
     m_listener = m_injector.getInstance(MockEventListener.class);
     m_definition = EasyMock.createNiceMock(AlertDefinitionEntity.class);
+    m_alertHelper = m_injector.getInstance(AlertHelper.class);
 
     // !!! need a synchronous op for testing
     m_synchronizedBus = new EventBus();
@@ -146,6 +164,12 @@ public class StaleAlertRunnableTest {
     expect(m_alertsDao.findCurrentByCluster(CLUSTER_ID)).andReturn(
         m_currentAlerts).atLeastOnce();
 
+    m_host = createNiceMock(Host.class);
+    expect(m_host.getHostId()).andReturn(1L);
+    expect(m_host.getState()).andReturn(HostState.HEALTHY);
+
+    expect(m_cluster.getHost(anyString())).andReturn(m_host).anyTimes();
+
     // mock out the uptime to be a while (since most tests are not testing
     // system uptime)
     m_runtimeMXBean = EasyMock.createNiceMock(RuntimeMXBean.class);
@@ -154,8 +178,12 @@ public class StaleAlertRunnableTest {
     PowerMock.replay(ManagementFactory.class);
     expect(m_runtimeMXBean.getUptime()).andReturn(360000L);
 
-    replay(m_definition, m_cluster, m_clusters,
-        m_definitionDao, m_alertsDao, m_runtimeMXBean);
+    
expect(m_alertHelper.getHostIdsByDefinitionId(anyLong())).andReturn(Collections.emptyList()).anyTimes();
+    
expect(m_alertHelper.getWaitFactorMultiplier(anyObject(AlertDefinition.class))).andReturn(2).anyTimes();
+    
expect(m_alertHelper.getStaleAlerts(anyLong())).andReturn(Collections.EMPTY_MAP).anyTimes();
+
+    replay(m_host, m_definition, m_cluster, m_clusters,
+        m_definitionDao, m_alertsDao, m_runtimeMXBean, m_alertHelper);
     }
 
   /**
@@ -165,6 +193,78 @@ public class StaleAlertRunnableTest {
   public void teardown() throws Exception {
   }
 
+  @Test
+  public void testPrepareHostDefinitions() {
+    StaleAlertRunnable runnable = new 
StaleAlertRunnable(m_definition.getDefinitionName());
+    AlertDefinitionsHolder alertDefinitionHolder = 
m_injector.getInstance(AlertDefinitionsHolder.class);
+
+    Long alertDefinitionId1 = 1L;
+    Long alertDefinitionId2 = 2L;
+    Long alertDefinitionId3 = 3L;
+    Long alertDefinitionId4 = 4L;
+    Long hostId1 = 1L;
+    Long hostId2 = 2L;
+    /*
+    * host1:
+    *   cluster1
+    *     alertDefinition1
+    *   cluster2
+    *     alertDefinition2
+    *
+    * host2:
+    *   cluster1
+    *     alertDefinition1
+    *     alertDefinition3
+    *   cluster2
+    *     alertDefinition4
+    */
+    AlertDefinition alertDefinition1 = new AlertDefinition();
+    alertDefinition1.setDefinitionId(alertDefinitionId1);
+    AlertDefinition alertDefinition2 = new AlertDefinition();
+    alertDefinition2.setDefinitionId(alertDefinitionId2);
+    AlertDefinition alertDefinition3 = new AlertDefinition();
+    alertDefinition3.setDefinitionId(alertDefinitionId3);
+    AlertDefinition alertDefinition4 = new AlertDefinition();
+    alertDefinition4.setDefinitionId(alertDefinitionId4);
+
+    AlertCluster alertCluster1host1 = new 
AlertCluster(Collections.singletonMap(alertDefinitionId1, alertDefinition1), 
"host1");
+    AlertCluster alertCluster2host1 = new 
AlertCluster(Collections.singletonMap(alertDefinitionId2, alertDefinition2), 
"host1");
+
+    AlertCluster alertCluster1host2 = new AlertCluster(new 
HashMap(){{put(alertDefinitionId3, alertDefinition3);
+      put(alertDefinitionId1, alertDefinition1);}}, "host2");
+    AlertCluster alertCluster2host2 = new 
AlertCluster(Collections.singletonMap(alertDefinitionId4, alertDefinition4), 
"host2");
+    AlertDefinitionsAgentUpdateEvent hostUpdate1 = new 
AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        new HashMap(){{put(1L, alertCluster1host1); put(2L, 
alertCluster2host1);}}, "host1", hostId1);
+    AlertDefinitionsAgentUpdateEvent hostUpdate2 = new 
AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        new HashMap(){{put(1L, alertCluster1host2); put(2L, 
alertCluster2host2);}}, "host2", hostId2);
+    alertDefinitionHolder.setData(hostUpdate1, 1L);
+    alertDefinitionHolder.setData(hostUpdate2, 2L);
+    m_injector.injectMembers(runnable);
+
+    Map<Long, List<Long>> alertDefinitionsToHost = 
runnable.prepareHostDefinitions(hostId1);
+    assertEquals(2, alertDefinitionsToHost.size());
+
+    assertNotNull(alertDefinitionsToHost.get(alertDefinitionId1));
+    assertEquals(2, alertDefinitionsToHost.get(alertDefinitionId1).size());
+    
assertTrue(alertDefinitionsToHost.get(alertDefinitionId1).contains(hostId1));
+    
assertTrue(alertDefinitionsToHost.get(alertDefinitionId1).contains(hostId2));
+
+    assertNotNull(alertDefinitionsToHost.get(alertDefinitionId3));
+    assertEquals(1, alertDefinitionsToHost.get(alertDefinitionId3).size());
+    assertEquals(Long.valueOf(hostId2), 
alertDefinitionsToHost.get(alertDefinitionId3).get(0));
+
+    alertDefinitionsToHost = runnable.prepareHostDefinitions(hostId2);
+    assertEquals(2, alertDefinitionsToHost.size());
+
+    assertNotNull(alertDefinitionsToHost.get(alertDefinitionId2));
+    assertEquals(1, alertDefinitionsToHost.get(alertDefinitionId2).size());
+    assertEquals(Long.valueOf(hostId1), 
alertDefinitionsToHost.get(alertDefinitionId2).get(0));
+
+    assertNotNull(alertDefinitionsToHost.get(alertDefinitionId4));
+    assertEquals(1, alertDefinitionsToHost.get(alertDefinitionId4).size());
+    assertEquals(Long.valueOf(hostId2), 
alertDefinitionsToHost.get(alertDefinitionId4).get(0));
+  }
+
   /**
    * Tests that the event is triggerd with a status of OK.
    */
@@ -183,6 +283,7 @@ public class StaleAlertRunnableTest {
     AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
 
     expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    expect(current1.getDefinitionId()).andReturn(1L).atLeastOnce();
     expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
 
     
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
@@ -203,27 +304,16 @@ public class StaleAlertRunnableTest {
     // run the alert
     runnable.run();
 
-    assertEquals(1,
-        m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
-
-    List<AlertEvent> events = 
m_listener.getAlertEventInstances(AlertReceivedEvent.class);
-    assertEquals(1, events.size());
-
-    AlertReceivedEvent event = (AlertReceivedEvent) events.get(0);
-    Alert alert = event.getAlert();
-    assertEquals("AMBARI", alert.getService());
-    assertEquals("AMBARI_SERVER", alert.getComponent());
-    assertEquals(AlertState.OK, alert.getState());
-    assertEquals(DEFINITION_NAME, alert.getName());
+    checkSingleEventToState(AlertState.OK);
 
-    verify(m_cluster, m_clusters, m_definitionDao);
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
   }
 
   /**
    * Tests that a stale alert triggers the event with a status of CRITICAL.
    */
   @Test
-  public void testStaleAlert() {
+  public void testAmbariStaleAlert() {
     // create current alerts that are not stale
     AlertDefinitionEntity definition = new AlertDefinitionEntity();
     definition.setClusterId(CLUSTER_ID);
@@ -238,6 +328,7 @@ public class StaleAlertRunnableTest {
     AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
 
     expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    expect(current1.getDefinitionId()).andReturn(1L).atLeastOnce();
     expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
 
     // a really old timestampt to trigger the alert
@@ -259,28 +350,76 @@ public class StaleAlertRunnableTest {
     // run the alert
     runnable.run();
 
-    assertEquals(1,
+    checkSingleEventToState(AlertState.CRITICAL);
+
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
+  }
+
+  /**
+   * Tests that a stale message from agent triggers the event with a status of 
CRITICAL.
+   */
+  @Test
+  public void testStaleAlertFromAgent() {
+    Long alertDefinitionId = 1L;
+
+    // create current alerts that are not stale
+    AlertDefinitionEntity definition = new AlertDefinitionEntity();
+    definition.setClusterId(CLUSTER_ID);
+    definition.setDefinitionName("foo-definition");
+    definition.setServiceName("HDFS");
+    definition.setComponentName("NAMENODE");
+    definition.setEnabled(true);
+    definition.setScheduleInterval(1);
+
+    // create current alerts that are stale
+    AlertCurrentEntity current1 = createNiceMock(AlertCurrentEntity.class);
+    AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
+
+    expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    
expect(current1.getDefinitionId()).andReturn(alertDefinitionId).atLeastOnce();
+    expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
+    expect(history1.getHostName()).andReturn("host1").atLeastOnce();
+
+    reset(m_alertHelper);
+    
expect(m_alertHelper.getWaitFactorMultiplier(anyObject(AlertDefinition.class))).andReturn(2).anyTimes();
+    
expect(m_alertHelper.getStaleAlerts(anyLong())).andReturn(Collections.singletonMap(alertDefinitionId,
 0L)).atLeastOnce();
+
+    
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
+    
expect(current1.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
+
+    replay(current1, history1, m_alertHelper);
+
+    m_currentAlerts.add(current1);
+
+    // precondition that no events were fired
+    assertEquals(0,
         m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
 
-    List<AlertEvent> events = 
m_listener.getAlertEventInstances(AlertReceivedEvent.class);
-    assertEquals(1, events.size());
+    // instantiate and inject mocks
+    StaleAlertRunnable runnable = new 
StaleAlertRunnable(m_definition.getDefinitionName());
+    m_injector.injectMembers(runnable);
 
-    AlertReceivedEvent event = (AlertReceivedEvent) events.get(0);
-    Alert alert = event.getAlert();
-    assertEquals("AMBARI", alert.getService());
-    assertEquals("AMBARI_SERVER", alert.getComponent());
-    assertEquals(AlertState.CRITICAL, alert.getState());
-    assertEquals(DEFINITION_NAME, alert.getName());
+    // run the alert
+    runnable.run();
+
+    checkSingleEventToState(AlertState.CRITICAL);
 
-    verify(m_cluster, m_clusters, m_definitionDao);
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
   }
 
-  /**
-   * Tests that a stale alert in maintenance mode doesn't trigger the event.
+  /**<AlertCurrentEntity> currentAlerts = m_alertsDao.findCurrentByCluster(
+        cluster.getClusterId());
+
+    long now = System.currentTimeMillis();
+
+    Map<Long, List<Long>> alertDefinitionsToHosts = 
prepareHostDefinitions(cluster.getClusterId());
+   * Tests that a heartbeat loose triggers the event with a status of CRITICAL.
    */
   @Test
-  public void testStaleAlertInMaintenaceMode() {
-    // create current alerts that are stale
+  public void testStaleAlertHeartbeatLost() {
+    Long alertDefinitionId = 1L;
+
+    // create current alerts that are not stale
     AlertDefinitionEntity definition = new AlertDefinitionEntity();
     definition.setClusterId(CLUSTER_ID);
     definition.setDefinitionName("foo-definition");
@@ -289,30 +428,29 @@ public class StaleAlertRunnableTest {
     definition.setEnabled(true);
     definition.setScheduleInterval(1);
 
-    // create current alerts where 1 is stale but in maintence mode
+    // create current alerts that are stale
     AlertCurrentEntity current1 = createNiceMock(AlertCurrentEntity.class);
     AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
-    AlertCurrentEntity current2 = createNiceMock(AlertCurrentEntity.class);
-    AlertHistoryEntity history2 = createNiceMock(AlertHistoryEntity.class);
 
     expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    
expect(current1.getDefinitionId()).andReturn(alertDefinitionId).atLeastOnce();
     expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
+    expect(history1.getHostName()).andReturn("host1").atLeastOnce();
 
-    expect(current2.getAlertHistory()).andReturn(history2).atLeastOnce();
-    expect(history2.getAlertDefinition()).andReturn(definition).atLeastOnce();
+    
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
+    
expect(current1.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
 
-    // maintenance mode with a really old timestamp
-    
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.ON).atLeastOnce();
-    expect(current1.getLatestTimestamp()).andReturn(1L).atLeastOnce();
+    reset(m_cluster, m_host);
+    m_host = createNiceMock(Host.class);
+    expect(m_host.getHostId()).andReturn(1L);
+    expect(m_host.getState()).andReturn(HostState.HEARTBEAT_LOST);
 
-    // an that that is not stale
-    
expect(current2.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
-    
expect(current2.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
+    expect(m_cluster.getClusterId()).andReturn(CLUSTER_ID).atLeastOnce();
+    expect(m_cluster.getHost(anyString())).andReturn(m_host).anyTimes();
 
-    replay(current1, history1, current2, history2);
+    replay(current1, history1, m_host, m_cluster);
 
     m_currentAlerts.add(current1);
-    m_currentAlerts.add(current2);
 
     // precondition that no events were fired
     assertEquals(0,
@@ -325,63 +463,137 @@ public class StaleAlertRunnableTest {
     // run the alert
     runnable.run();
 
-    assertEquals(1,
+    checkSingleEventToState(AlertState.CRITICAL);
+
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
+  }
+
+  /**
+   * Tests alerts with ignoreHost == true. One host is in HEARTBEAT_LOST state.
+   * host1:
+   *   cluster1
+   *     alertDefinition1
+   *
+   * host2:
+   *   cluster1
+   *     alertDefinition1
+   */
+  @Test
+  public void testStaleAlertWithHostIgnore() {
+    Long alertDefinitionId = 1L;
+    prepareAlertHolderWithHostAlert(alertDefinitionId);
+
+    // create current alerts that are not stale
+    AlertDefinitionEntity definition = new AlertDefinitionEntity();
+    definition.setClusterId(CLUSTER_ID);
+    definition.setDefinitionName("foo-definition");
+    definition.setServiceName("HDFS");
+    definition.setComponentName("NAMENODE");
+    definition.setEnabled(true);
+    definition.setScheduleInterval(1);
+
+    // create current alerts that are stale
+    AlertCurrentEntity current1 = createNiceMock(AlertCurrentEntity.class);
+    AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
+
+    expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    
expect(current1.getDefinitionId()).andReturn(alertDefinitionId).atLeastOnce();
+    expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
+
+    
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
+    
expect(current1.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
+
+    reset(m_cluster);
+    Host host1 = createNiceMock(Host.class);
+    Host host2 = createNiceMock(Host.class);
+    expect(host1.getHostId()).andReturn(1L);
+    expect(host1.getState()).andReturn(HostState.HEARTBEAT_LOST).atLeastOnce();
+    expect(host1.getLastHeartbeatTime()).andReturn(1L);
+    expect(host2.getHostId()).andReturn(2L);
+    expect(host2.getState()).andReturn(HostState.HEALTHY).atLeastOnce();
+    expect(host2.getLastHeartbeatTime()).andReturn(2L);
+
+    expect(m_cluster.getClusterId()).andReturn(CLUSTER_ID).atLeastOnce();
+    expect(m_cluster.getHost(eq(1L))).andReturn(host1).anyTimes();
+    expect(m_cluster.getHost(eq(2L))).andReturn(host2).anyTimes();
+
+    replay(current1, history1, host1, host2, m_cluster);
+
+    m_currentAlerts.add(current1);
+
+    // precondition that no events were fired
+    assertEquals(0,
         m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
 
-    List<AlertEvent> events = 
m_listener.getAlertEventInstances(AlertReceivedEvent.class);
-    assertEquals(1, events.size());
+    // instantiate and inject mocks
+    StaleAlertRunnable runnable = new 
StaleAlertRunnable(m_definition.getDefinitionName());
+    m_injector.injectMembers(runnable);
 
-    AlertReceivedEvent event = (AlertReceivedEvent) events.get(0);
-    Alert alert = event.getAlert();
-    assertEquals("AMBARI", alert.getService());
-    assertEquals("AMBARI_SERVER", alert.getComponent());
-    assertEquals(AlertState.OK, alert.getState());
-    assertEquals(DEFINITION_NAME, alert.getName());
+    // run the alert
+    runnable.run();
 
-    verify(m_cluster, m_clusters, m_definitionDao);
+    checkSingleEventToState(AlertState.OK);
+
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
   }
 
+
   /**
-   * Tests that stale alerts are not reported if the server has not be running
-   * long enough.
+   * Tests alerts with ignoreHost == true. Both hosts are in HEARTBEAT_LOST 
state.
+   * host1:
+   *   cluster1
+   *     alertDefinition1
+   *
+   * host2:
+   *   cluster1
+   *     alertDefinition1
    */
   @Test
-  public void testStaleAlertWithServerUptime() {
-    // reset the Runtime MX bean to a low value
-    reset(m_runtimeMXBean);
-    expect(m_runtimeMXBean.getUptime()).andReturn(1000L);
-    replay(m_runtimeMXBean);
+  public void testStaleAlertWithHostIgnoreCritical() {
+    Long alertDefinitionId = 1L;
+    prepareAlertHolderWithHostAlert(alertDefinitionId);
 
-    // create current alerts that are stale (5 minute interval)
+    // create current alerts that are not stale
     AlertDefinitionEntity definition = new AlertDefinitionEntity();
     definition.setClusterId(CLUSTER_ID);
     definition.setDefinitionName("foo-definition");
     definition.setServiceName("HDFS");
     definition.setComponentName("NAMENODE");
     definition.setEnabled(true);
-    definition.setScheduleInterval(5);
+    definition.setScheduleInterval(1);
 
     // create current alerts that are stale
     AlertCurrentEntity current1 = createNiceMock(AlertCurrentEntity.class);
     AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
 
     expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    
expect(current1.getDefinitionId()).andReturn(alertDefinitionId).atLeastOnce();
     expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
 
-    // use a timestamp that would trigger the alert, say 3x the interval ago 
(so
-    // 15 minutes ago)
-    long now = System.currentTimeMillis();
-    long staleTime = now - (definition.getScheduleInterval() * 60 * 1000 * 3);
-
     
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
-    expect(current1.getLatestTimestamp()).andReturn(staleTime).atLeastOnce();
+    
expect(current1.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
 
-    replay(current1, history1);
+    reset(m_cluster);
+    Host host1 = createNiceMock(Host.class);
+    Host host2 = createNiceMock(Host.class);
+    expect(host1.getHostId()).andReturn(1L);
+    expect(host1.getState()).andReturn(HostState.HEARTBEAT_LOST).atLeastOnce();
+    expect(host1.getLastHeartbeatTime()).andReturn(1L);
+    expect(host2.getHostId()).andReturn(2L);
+    expect(host2.getState()).andReturn(HostState.HEARTBEAT_LOST).atLeastOnce();
+    expect(host2.getLastHeartbeatTime()).andReturn(2L);
+
+    expect(m_cluster.getClusterId()).andReturn(CLUSTER_ID).atLeastOnce();
+    expect(m_cluster.getHost(eq(1L))).andReturn(host1).anyTimes();
+    expect(m_cluster.getHost(eq(2L))).andReturn(host2).anyTimes();
+
+    replay(current1, history1, host1, host2, m_cluster);
 
     m_currentAlerts.add(current1);
 
     // precondition that no events were fired
-    assertEquals(0, 
m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+    assertEquals(0,
+        m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
 
     // instantiate and inject mocks
     StaleAlertRunnable runnable = new 
StaleAlertRunnable(m_definition.getDefinitionName());
@@ -390,45 +602,98 @@ public class StaleAlertRunnableTest {
     // run the alert
     runnable.run();
 
-    // ensure that our mock MX bean was used
-    verify(m_runtimeMXBean);
+    checkSingleEventToState(AlertState.CRITICAL);
 
-    // verify that our uptime was too short so nothing should have been
-    // triggered
-    assertEquals(1, 
m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
-    List<AlertEvent> events = 
m_listener.getAlertEventInstances(AlertReceivedEvent.class);
-    assertEquals(1, events.size());
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
+  }
 
-    AlertReceivedEvent event = (AlertReceivedEvent) events.get(0);
-    Alert alert = event.getAlert();
-    assertEquals("AMBARI", alert.getService());
-    assertEquals("AMBARI_SERVER", alert.getComponent());
-    assertEquals(AlertState.OK, alert.getState());
-    assertEquals(DEFINITION_NAME, alert.getName());
+  private void prepareAlertHolderWithHostAlert(Long alertDefinitionId) {
+    AlertDefinitionsHolder alertDefinitionHolder = 
m_injector.getInstance(AlertDefinitionsHolder.class);
+    Long hostId1 = 1L;
+    Long hostId2 = 2L;
+
+    AlertDefinition alertDefinition1 = new AlertDefinition();
+    alertDefinition1.setDefinitionId(alertDefinitionId);
+
+    AlertCluster alertCluster1host1 = new 
AlertCluster(Collections.singletonMap(alertDefinitionId, alertDefinition1), 
"host1");
+
+    AlertCluster alertCluster1host2 = new 
AlertCluster(Collections.singletonMap(alertDefinitionId, alertDefinition1), 
"host2");
+    AlertDefinitionsAgentUpdateEvent hostUpdate1 = new 
AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        Collections.singletonMap(1L, alertCluster1host1), "host1", hostId1);
+    AlertDefinitionsAgentUpdateEvent hostUpdate2 = new 
AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        Collections.singletonMap(1L, alertCluster1host2), "host2", hostId2);
+    alertDefinitionHolder.setData(hostUpdate1, 1L);
+    alertDefinitionHolder.setData(hostUpdate2, 2L);
+  }
 
-    // now reset the mocks to indicate that Ambari has been up long enough
-    m_listener.reset();
-    long uptime = definition.getScheduleInterval() * 60 * 1000 * 4;
-    reset(m_runtimeMXBean);
-    expect(m_runtimeMXBean.getUptime()).andReturn(uptime);
-    replay(m_runtimeMXBean);
+  /**
+   * Tests that a stale alert in maintenance mode doesn't trigger the event.
+   */
+  @Test
+  public void testStaleAlertInMaintenaceMode() {
+    // create current alerts that are stale
+    AlertDefinitionEntity definition = new AlertDefinitionEntity();
+    definition.setClusterId(CLUSTER_ID);
+    definition.setDefinitionName("foo-definition");
+    definition.setServiceName("HDFS");
+    definition.setComponentName("NAMENODE");
+    definition.setEnabled(true);
+    definition.setScheduleInterval(1);
+
+    // create current alerts where 1 is stale but in maintence mode
+    AlertCurrentEntity current1 = createNiceMock(AlertCurrentEntity.class);
+    AlertHistoryEntity history1 = createNiceMock(AlertHistoryEntity.class);
+    AlertCurrentEntity current2 = createNiceMock(AlertCurrentEntity.class);
+    AlertHistoryEntity history2 = createNiceMock(AlertHistoryEntity.class);
+
+    expect(current1.getAlertHistory()).andReturn(history1).atLeastOnce();
+    expect(history1.getAlertDefinition()).andReturn(definition).atLeastOnce();
+
+    expect(current2.getAlertHistory()).andReturn(history2).atLeastOnce();
+    expect(history2.getAlertDefinition()).andReturn(definition).atLeastOnce();
+
+    // maintenance mode with a really old timestamp
+    
expect(current1.getMaintenanceState()).andReturn(MaintenanceState.ON).atLeastOnce();
+    expect(current1.getLatestTimestamp()).andReturn(1L).atLeastOnce();
 
-    // run the alert again and verify that the same stale alert caused a
-    // CRITICAL
+    // an that that is not stale
+    
expect(current2.getMaintenanceState()).andReturn(MaintenanceState.OFF).atLeastOnce();
+    
expect(current2.getLatestTimestamp()).andReturn(System.currentTimeMillis()).atLeastOnce();
+
+    replay(current1, history1, current2, history2);
+
+    m_currentAlerts.add(current1);
+    m_currentAlerts.add(current2);
+
+    // precondition that no events were fired
+    assertEquals(0,
+        m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+
+    // instantiate and inject mocks
+    StaleAlertRunnable runnable = new 
StaleAlertRunnable(m_definition.getDefinitionName());
+    m_injector.injectMembers(runnable);
+
+    // run the alert
     runnable.run();
 
-    // recheck for the stale alert
-    events = m_listener.getAlertEventInstances(AlertReceivedEvent.class);
+    checkSingleEventToState(AlertState.OK);
+
+    verify(m_cluster, m_clusters, m_definitionDao, m_alertHelper);
+  }
+
+  private void checkSingleEventToState(AlertState alertState) {
+    assertEquals(1,
+        m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+
+    List<AlertEvent> events = 
m_listener.getAlertEventInstances(AlertReceivedEvent.class);
     assertEquals(1, events.size());
 
-    event = (AlertReceivedEvent) events.get(0);
-    alert = event.getAlert();
+    AlertReceivedEvent event = (AlertReceivedEvent) events.get(0);
+    Alert alert = event.getAlert();
     assertEquals("AMBARI", alert.getService());
     assertEquals("AMBARI_SERVER", alert.getComponent());
-    assertEquals(AlertState.CRITICAL, alert.getState());
+    assertEquals(alertState, alert.getState());
     assertEquals(DEFINITION_NAME, alert.getName());
-
-    assertEquals(1, 
m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
   }
 
   /**
@@ -445,6 +710,7 @@ public class StaleAlertRunnableTest {
 
       binder.bind(AlertsDAO.class).toInstance(createNiceMock(AlertsDAO.class));
       
binder.bind(HostRoleCommandDAO.class).toInstance(createNiceMock(HostRoleCommandDAO.class));
+      
binder.bind(AlertHelper.class).toInstance(createNiceMock(AlertHelper.class));
     }
   }
 }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
index d1da9f3..70ce75a 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
@@ -157,7 +157,7 @@ public class AlertReceivedListenerTest {
    * Tests that a disabled definition doesn't record alert events.
    */
   @Test
-  public void testDisabledAlert() {
+  public void testDisabledAlert() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String componentName = "DATANODE";
 
@@ -199,7 +199,7 @@ public class AlertReceivedListenerTest {
    * Tests an invalid host is being reported in an alert.
    */
   @Test
-  public void testInvalidHost() {
+  public void testInvalidHost() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String componentName = "DATANODE";
 
@@ -237,7 +237,7 @@ public class AlertReceivedListenerTest {
    * Tests that a disabled definition doesn't record alert events.
    */
   @Test
-  public void testInvalidAlertDefinition() {
+  public void testInvalidAlertDefinition() throws AmbariException {
     String componentName = "DATANODE";
 
     Alert alert = new Alert("missing_alert_definition_name", null, "HDFS",
@@ -261,7 +261,7 @@ public class AlertReceivedListenerTest {
    * Tests an invalid pairing of component to host.
    */
   @Test
-  public void testInvalidServiceComponentHost() {
+  public void testInvalidServiceComponentHost() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String componentName = "DATANODE";
 
@@ -346,7 +346,7 @@ public class AlertReceivedListenerTest {
    * Tests that an invalid host from a host-level agent alert is rejected.
    */
   @Test
-  public void testAgentAlertFromInvalidHost() {
+  public void testAgentAlertFromInvalidHost() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String serviceName = RootService.AMBARI.name();
     String componentName = RootComponent.AMBARI_AGENT.name();
@@ -385,7 +385,7 @@ public class AlertReceivedListenerTest {
    * Tests that an alert for AMBARI/AMBARI_SERVER is always valid.
    */
   @Test
-  public void testAmbariServerValidAlerts() {
+  public void testAmbariServerValidAlerts() throws AmbariException {
     String definitionName = AMBARI_ALERT_DEFINITION;
     String serviceName = RootService.AMBARI.name();
     String componentName = RootComponent.AMBARI_SERVER.name();
@@ -426,7 +426,7 @@ public class AlertReceivedListenerTest {
    * alert.
    */
   @Test
-  public void testMissingClusterAndInvalidHost() {
+  public void testMissingClusterAndInvalidHost() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String serviceName = RootService.AMBARI.name();
     String componentName = RootComponent.AMBARI_AGENT.name();
@@ -467,7 +467,7 @@ public class AlertReceivedListenerTest {
    * if there is currently no current alert.
    */
   @Test
-  public void testSkippedAlertWithNoCurrentAlert() {
+  public void testSkippedAlertWithNoCurrentAlert() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String serviceName = "HDFS";
     String componentName = "NAMENODE";
@@ -493,7 +493,7 @@ public class AlertReceivedListenerTest {
    * create an entry if there is currently no current alert.
    */
   @Test
-  public void testSkippedAlertUpdatesTimestampAndText() {
+  public void testSkippedAlertUpdatesTimestampAndText() throws AmbariException 
{
     String definitionName = ALERT_DEFINITION + "1";
     String serviceName = "HDFS";
     String componentName = "NAMENODE";
@@ -550,7 +550,7 @@ public class AlertReceivedListenerTest {
    * Tests that we correctly record alert occurance information.
    */
   @Test
-  public void testAlertOccurrences() {
+  public void testAlertOccurrences() throws AmbariException {
     String definitionName = ALERT_DEFINITION + "1";
     String serviceName = "HDFS";
     String componentName = "NAMENODE";
@@ -931,7 +931,11 @@ public class AlertReceivedListenerTest {
           alert.setTimestamp(System.currentTimeMillis());
 
           final AlertReceivedEvent event = new 
AlertReceivedEvent(m_cluster.getClusterId(), alert);
-          listener.onAlertEvent(event);
+          try {
+            listener.onAlertEvent(event);
+          } catch (AmbariException e) {
+            e.printStackTrace();
+          }
         }
       };
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
index 83f565f..0ca170b 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
@@ -159,7 +159,7 @@ public class AlertDataManagerTest {
   }
 
   @Test
-  public void testAlertRecords() {
+  public void testAlertRecords() throws AmbariException {
     Alert alert1 = new Alert(ALERT_DEFINITION, null, SERVICE, COMPONENT, HOST1,
         AlertState.OK);
     alert1.setLabel(ALERT_LABEL);

-- 
To stop receiving notification emails like this one, please contact
aonis...@apache.org.

Reply via email to