ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jonathanhur...@apache.org
Subject ambari git commit: AMBARI-15620 - Orphaned Host Alerts Cause Stale Alert Notifications After Removing Hosts (jonathanhurley)
Date Wed, 30 Mar 2016 19:59:48 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk 46a34ccde -> ef30868be


AMBARI-15620 - Orphaned Host Alerts Cause Stale Alert Notifications After Removing Hosts (jonathanhurley)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/ef30868b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/ef30868b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/ef30868b

Branch: refs/heads/trunk
Commit: ef30868be7e5c0300bf27f0a6a62d2277d5f68a6
Parents: 46a34cc
Author: Jonathan Hurley <jhurley@hortonworks.com>
Authored: Tue Mar 29 15:29:18 2016 -0400
Committer: Jonathan Hurley <jhurley@hortonworks.com>
Committed: Wed Mar 30 15:59:41 2016 -0400

----------------------------------------------------------------------
 .../server/alerts/StaleAlertRunnable.java       |  33 +++--
 .../listeners/alerts/AlertReceivedListener.java |  75 ++++++++++--
 .../apache/ambari/server/orm/dao/HostDAO.java   |  27 ++--
 .../apache/ambari/server/state/Clusters.java    |  66 ++++++----
 .../server/state/cluster/ClustersImpl.java      |  59 ++++++---
 .../state/alerts/AlertReceivedListenerTest.java | 122 +++++++++++++++++++
 6 files changed, 300 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/main/java/org/apache/ambari/server/alerts/StaleAlertRunnable.java
----------------------------------------------------------------------
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 83abc8e..54abd62 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
@@ -19,10 +19,10 @@ package org.apache.ambari.server.alerts;
 
 import java.text.MessageFormat;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 import org.apache.ambari.server.events.AlertEvent;
 import org.apache.ambari.server.events.AlertReceivedEvent;
@@ -74,11 +74,11 @@ public class StaleAlertRunnable implements Runnable {
   /**
    * The message to use when alerts are detected as stale.
    */
-  private static final String STALE_ALERTS_MSG = "There are {0} stale alerts from {1} host(s):
{2}";
+  private static final String STALE_ALERTS_MSG = "There are {0} stale alerts from {1} host(s):\n{2}";
 
   private static final String TIMED_LABEL_MSG = "{0} ({1})";
 
-  private static final String HOST_LABEL_MSG = "{0}[{1}]";
+  private static final String HOST_LABEL_MSG = "{0}\n  [{1}]";
 
   /**
    * Convert the minutes for the delay of an alert into milliseconds.
@@ -134,9 +134,9 @@ public class StaleAlertRunnable implements Runnable {
         }
 
         long now = System.currentTimeMillis();
-        Set<String> staleAlerts = new HashSet<String>();
+        Set<String> staleAlerts = new TreeSet<String>();
         Map<String, Set<String>> staleHostAlerts = new HashMap<>();
-        Set<String> hostsWithStaleAlerts = new HashSet<String>();
+        Set<String> hostsWithStaleAlerts = new TreeSet<>();
 
         // get the cluster's current alerts
         List<AlertCurrentEntity> currentAlerts = m_alertsDao.findCurrentByCluster(cluster.getClusterId());
@@ -174,13 +174,20 @@ public class StaleAlertRunnable implements Runnable {
           // if the last time it was run is >= 2x the interval, it's stale
           long timeDifference = now - current.getLatestTimestamp();
           if (timeDifference >= 2 * intervalInMillis) {
+
+            // it is technically possible to have a null/blank label; if so,
+            // default to the name of the definition
             String label = definition.getLabel();
+            if (StringUtils.isEmpty(label)) {
+              label = definition.getDefinitionName();
+            }
+
             if (null != history.getHostName()) {
               // keep track of the host, if not null
               String hostName = history.getHostName();
               hostsWithStaleAlerts.add(hostName);
               if(!staleHostAlerts.containsKey(hostName)) {
-                staleHostAlerts.put(hostName, new HashSet<String>());
+                staleHostAlerts.put(hostName, new TreeSet<String>());
               }
 
               staleHostAlerts.get(hostName).add(MessageFormat.format(TIMED_LABEL_MSG, label,
@@ -193,7 +200,8 @@ public class StaleAlertRunnable implements Runnable {
         }
 
         for(String host : staleHostAlerts.keySet()) {
-          staleAlerts.add(MessageFormat.format(HOST_LABEL_MSG, host, StringUtils.join(staleHostAlerts.get(host),
", ")));
+          staleAlerts.add(MessageFormat.format(HOST_LABEL_MSG, host,
+              StringUtils.join(staleHostAlerts.get(host), ",\n  ")));
         }
 
         AlertState alertState = AlertState.OK;
@@ -205,7 +213,7 @@ public class StaleAlertRunnable implements Runnable {
           alertState = AlertState.CRITICAL;
           alertText = MessageFormat.format(STALE_ALERTS_MSG,
               staleAlerts.size(), hostsWithStaleAlerts.size(),
-              StringUtils.join(staleAlerts, ", "));
+              StringUtils.join(staleAlerts, ",\n"));
         }
 
         Alert alert = new Alert(entity.getDefinitionName(), null,
@@ -245,12 +253,15 @@ public class StaleAlertRunnable implements Runnable {
     hour = min / MINUTES_PER_HOUR;
     min = min % MINUTES_PER_HOUR;
     String result = "";
-    if(days > 0)
+    if(days > 0) {
       result += days + "d ";
-    if(hour > 0)
+    }
+    if(hour > 0) {
       result += hour + "h ";
-    if(min > 0)
+    }
+    if(min > 0) {
       result += min + "m ";
+    }
     return result.trim();
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
----------------------------------------------------------------------
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 8dc8e1e..f6aa1aa 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
@@ -25,6 +25,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
+import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
 import org.apache.ambari.server.controller.RootServiceResponseFactory.Services;
 import org.apache.ambari.server.events.AlertEvent;
 import org.apache.ambari.server.events.AlertReceivedEvent;
@@ -318,9 +319,15 @@ public class AlertReceivedListener {
 
   /**
    * Gets whether the specified alert is valid for its reported cluster,
-   * service, component, and host. This method is necessary for the case where a
-   * component has been removed from a host, but the alert data is going to be
-   * returned before the agent alert job can be unscheduled.
+   * service, component, and host. This method is necessary for the following
+   * cases
+   * <ul>
+   * <li>A service/component is removed, but an alert queued for reporting is
+   * received after that event.</li>
+   * <li>A host is removed from the cluster but the agent is still running and
+   * reporting</li>
+   * <li>A cluster is renamed</li>
+   * </ul>
    *
    * @param alert
    *          the alert.
@@ -333,18 +340,36 @@ public class AlertReceivedListener {
     String componentName = alert.getComponent();
     String hostName = alert.getHostName();
 
-    // if the alert is not bound to a cluster, then it's most likely a
-    // host alert and is always valid
-    if( null == clusterName ){
+    // AMBARI/AMBARI_SERVER is always a valid service/component combination
+    String ambariServiceName = Services.AMBARI.name();
+    String ambariServerComponentName = Components.AMBARI_SERVER.name();
+    String ambariAgentComponentName = Components.AMBARI_AGENT.name();
+    if (ambariServiceName.equals(serviceName) && ambariServerComponentName.equals(componentName))
{
       return true;
     }
 
-    // AMBARI is always a valid service
-    String ambariServiceName = Services.AMBARI.name();
-    if (ambariServiceName.equals(serviceName)) {
+    // if the alert is not bound to a cluster, then it's most likely a
+    // host alert and is always valid as long as the host exists
+    if (StringUtils.isBlank(clusterName)) {
+      // no cluster, no host; return true out of respect for the unknown alert
+      if (StringUtils.isBlank(hostName)) {
+        return true;
+      }
+
+      // if a host is reported, it must be registered to some cluster somewhere
+      if (!m_clusters.get().hostExists(hostName)) {
+        LOG.error("Unable to process alert {} for an invalid host named {}",
+            alert.getName(), hostName);
+        return false;
+      }
+
+      // no cluster, valid host; return true
       return true;
     }
 
+    // at this point the following criteria is guaranteed, so get the cluster
+    // - a cluster exists
+    // - this is not for AMBARI_SERVER component
     final Cluster cluster;
     try {
       cluster = m_clusters.get().getCluster(clusterName);
@@ -366,24 +391,50 @@ public class AlertReceivedListener {
       return false;
     }
 
+    // at this point the following criteria is guaranteed
+    // - a cluster exists
+    // - this is not for AMBARI_SERVER component
+    //
+    // if the alert is for AMBARI/AMBARI_AGENT, then it's valid IFF
+    // the agent's host is still a part of the reported cluster
+    if (ambariServiceName.equals(serviceName) && ambariAgentComponentName.equals(componentName))
{
+      // agents MUST report a hostname
+      if (StringUtils.isBlank(hostName) || !m_clusters.get().hostExists(hostName)
+          || !m_clusters.get().isHostMappedToCluster(clusterName, hostName)) {
+        LOG.warn(
+            "Unable to process alert {} for cluster {} and host {} because the host is not
a part of the cluster.",
+            alert.getName(), clusterName, hostName);
+
+        return false;
+      }
+
+      // AMBARI/AMBARI_AGENT and valid host; return true
+      return true;
+    }
+
+    // at this point the following criteria is guaranteed
+    // - a cluster exists
+    // - not for the AMBARI service
     if (StringUtils.isNotBlank(hostName)) {
       // if valid hostname
       if (!m_clusters.get().hostExists(hostName)) {
-        LOG.error("Unable to process alert {} for an invalid host named {}",
+        LOG.warn("Unable to process alert {} for an invalid host named {}",
             alert.getName(), hostName);
         return false;
       }
+
       if (!cluster.getServices().containsKey(serviceName)) {
-        LOG.error("Unable to process alert {} for an invalid service named {}",
+        LOG.warn("Unable to process alert {} for an invalid service named {}",
             alert.getName(), serviceName);
 
         return false;
       }
+
       // if the alert is for a host/component then verify that the component
       // is actually installed on that host
       if (null != componentName &&
           !cluster.getHosts(serviceName, componentName).contains(hostName)) {
-        LOG.error(
+        LOG.warn(
             "Unable to process alert {} for an invalid service {} and component {} on host
{}",
             alert.getName(), serviceName, componentName, hostName);
         return false;

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostDAO.java
index ebd29e3..d367eb3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostDAO.java
@@ -18,20 +18,22 @@
 
 package org.apache.ambari.server.orm.dao;
 
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.orm.RequiresSession;
-import org.apache.ambari.server.orm.entities.HostEntity;
-import org.apache.ambari.server.orm.entities.StageEntity;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.StageEntity;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
 
 @Singleton
 public class HostDAO {
@@ -113,11 +115,6 @@ public class HostDAO {
     entityManagerProvider.get().remove(merge(hostEntity));
   }
 
-  @Transactional
-  public void removeByName(String hostName) {
-    remove(findByName(hostName));
-  }
-
   public List<String> getHostNamesByHostIds(List<Long> hostIds) {
     List<String> hostNames = new ArrayList<String>();
     if (hostIds != null) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
index a1ebaba..59dcaf8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
@@ -18,12 +18,12 @@
 
 package org.apache.ambari.server.state;
 
-import org.apache.ambari.server.AmbariException;
-
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.ambari.server.AmbariException;
+
 /**
  * Single entity that tracks all clusters and hosts that are managed
  * by the Ambari server
@@ -37,7 +37,7 @@ public interface Clusters {
    * @param stackId
    *          the stack for the cluster (not {@code null}).
    */
-  public void addCluster(String clusterName, StackId stackId)
+  void addCluster(String clusterName, StackId stackId)
       throws AmbariException;
 
   /**
@@ -50,7 +50,7 @@ public interface Clusters {
    *          the cluster will be created with this security type.
    * @throws AmbariException
    */
-  public void addCluster(String clusterName, StackId stackId, SecurityType securityType)
+  void addCluster(String clusterName, StackId stackId, SecurityType securityType)
     throws AmbariException;
 
   /**
@@ -58,20 +58,20 @@ public interface Clusters {
    * @param clusterName Name of the Cluster to retrieve
    * @return  <code>Cluster</code> identified by the given name
    */
-  public Cluster getCluster(String clusterName)
+  Cluster getCluster(String clusterName)
       throws AmbariException;
 
   /**
    * Get all clusters
    * @return <code>Map</code> of clusters with cluster name as key
    */
-  public Map<String, Cluster> getClusters();
+  Map<String, Cluster> getClusters();
 
   /**
    * Get all hosts being tracked by the Ambari server
    * @return <code>List</code> of <code>Host</code>
    */
-  public List<Host> getHosts();
+  List<Host> getHosts();
 
   /**
    * Returns all the cluster names for this hostname
@@ -79,7 +79,7 @@ public interface Clusters {
    * @return List of cluster names
    * @throws AmbariException
    */
-  public Set<Cluster> getClustersForHost(String hostname)
+  Set<Cluster> getClustersForHost(String hostname)
       throws AmbariException;
 
 
@@ -89,14 +89,26 @@ public interface Clusters {
    * @return Host object
    * @throws AmbariException
    */
-  public Host getHost(String hostname) throws AmbariException;
+  Host getHost(String hostname) throws AmbariException;
 
   /**
    * Check if host exists
    * @param hostname Name of the host requested
    * @return is host exists
    */
-  public boolean hostExists(String hostname);
+  boolean hostExists(String hostname);
+
+  /**
+   * Gets whether the specified cluster has a mapping for the specified host.
+   *
+   * @param clusterName
+   *          the cluster (not {@code null}).
+   * @param hostName
+   *          the host (not {@code null}).
+   * @return {@code true} if the host belongs to the cluster, {@code false}
+   *         otherwise.
+   */
+  boolean isHostMappedToCluster(String clusterName, String hostName);
 
   /**
    * Get a Host object managed by this server
@@ -104,14 +116,14 @@ public interface Clusters {
    * @return Host object
    * @throws AmbariException
    */
-  public Host getHostById(Long hostId) throws AmbariException;
+  Host getHostById(Long hostId) throws AmbariException;
 
   /**
    * Add a Host object to be managed by this server
    * @param hostname Host to be added
    * @throws AmbariException
    */
-  public void addHost(String hostname) throws AmbariException;
+  void addHost(String hostname) throws AmbariException;
 
   /**
    * Map host to the given cluster.
@@ -120,7 +132,7 @@ public interface Clusters {
    * @param clusterName
    * @throws AmbariException
    */
-  public void mapHostToCluster(String hostname, String clusterName)
+  void mapHostToCluster(String hostname, String clusterName)
       throws AmbariException;
 
   /**
@@ -129,7 +141,7 @@ public interface Clusters {
    * @param clusterName
    * @throws AmbariException
    */
-  public void mapHostsToCluster(Set<String> hostnames, String clusterName)
+  void mapHostsToCluster(Set<String> hostnames, String clusterName)
       throws AmbariException;
 
   /**
@@ -138,7 +150,7 @@ public interface Clusters {
    * @param newName
    * @throws AmbariException
    */
-  public void updateClusterName(String oldName, String newName);
+  void updateClusterName(String oldName, String newName);
 
   /**
    * Gets the cluster using the id.
@@ -146,13 +158,13 @@ public interface Clusters {
    * @return <code>Cluster</code> identified by the identifier
    * @throws AmbariException
    */
-  public Cluster getClusterById(long id) throws AmbariException;
+  Cluster getClusterById(long id) throws AmbariException;
 
   /**
    * Produces a debug dump into the supplied string buffer
    * @param sb The string buffer to add the debug dump to
    */
-  public void debugDump(StringBuilder sb);
+  void debugDump(StringBuilder sb);
 
   /**
    * Gets all the hosts associated with the cluster
@@ -160,7 +172,7 @@ public interface Clusters {
    * @return <code>Map</code> containing host name and <code>Host</code>
    * @throws AmbariException
    */
-  public Map<String, Host> getHostsForCluster(String clusterName)
+  Map<String, Host> getHostsForCluster(String clusterName)
       throws AmbariException;
 
   /**
@@ -169,7 +181,7 @@ public interface Clusters {
    * @return <code>Map</code> containing host id and <code>Host</code>
    * @throws AmbariException
    */
-  public Map<Long, Host> getHostIdsForCluster(String clusterName)
+  Map<Long, Host> getHostIdsForCluster(String clusterName)
       throws AmbariException;
 
   /**
@@ -177,7 +189,7 @@ public interface Clusters {
    * @param clusterName The name of the cluster
    * @throws AmbariException
    */
-  public void deleteCluster(String clusterName)
+  void deleteCluster(String clusterName)
       throws AmbariException;
 
   /**
@@ -186,7 +198,7 @@ public interface Clusters {
    * @param stackId The identifier for the stack
    * @throws AmbariException
    */
-  public void setCurrentStackVersion(String clusterName, StackId stackId)
+  void setCurrentStackVersion(String clusterName, StackId stackId)
       throws AmbariException;
 
   /**
@@ -195,7 +207,7 @@ public interface Clusters {
    * @param hostAttributes
    * @throws AmbariException
    */
-  public void updateHostWithClusterAndAttributes(
+  void updateHostWithClusterAndAttributes(
       Map<String, Set<String>> hostsClusters, Map<String, Map<String, String>>
hostAttributes)
       throws AmbariException;
 
@@ -204,14 +216,14 @@ public interface Clusters {
    * @param hostname
    * @param clusterName
    */
-  public void unmapHostFromCluster(String hostname, String clusterName)
+  void unmapHostFromCluster(String hostname, String clusterName)
       throws AmbariException;
 
   /**
    * Removes a host.  Inverts {@link #addHost(String)}
    * @param hostname
    */
-  public void deleteHost(String hostname)
+  void deleteHost(String hostname)
       throws AmbariException;
 
   /**
@@ -224,7 +236,7 @@ public interface Clusters {
    *
    * @return true if access to the cluster is allowed
    */
-  public boolean checkPermission(String clusterName, boolean readOnly);
+  boolean checkPermission(String clusterName, boolean readOnly);
 
   /**
    * Add the given map of attributes to the session for the cluster identified by the given
name.
@@ -232,7 +244,7 @@ public interface Clusters {
    * @param name        the cluster name
    * @param attributes  the session attributes
    */
-  public void addSessionAttributes(String name, Map<String, Object> attributes);
+  void addSessionAttributes(String name, Map<String, Object> attributes);
 
   /**
    * Get the map of session attributes for the cluster identified by the given name.
@@ -241,5 +253,5 @@ public interface Clusters {
    *
    * @return the map of session attributes for the cluster; never null
    */
-  public Map<String, Object> getSessionAttributes(String name);
+  Map<String, Object> getSessionAttributes(String name);
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
index 6c68d0e..6c1e56e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
@@ -18,10 +18,20 @@
 
 package org.apache.ambari.server.state.cluster;
 
-import com.google.common.collect.Sets;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.persistence.RollbackException;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.ClusterNotFoundException;
 import org.apache.ambari.server.DuplicateResourceException;
@@ -82,18 +92,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.GrantedAuthority;
 
-import javax.persistence.RollbackException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
 
 @Singleton
 public class ClustersImpl implements Clusters {
@@ -378,6 +380,28 @@ public class ClustersImpl implements Clusters {
     return hosts.containsKey(hostname);
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isHostMappedToCluster(String clusterName, String hostName) {
+    checkLoaded();
+
+    r.lock();
+    try {
+      Set<Cluster> clusters = hostClusterMap.get(hostName);
+      for (Cluster cluster : clusters) {
+        if (clusterName.equals(cluster.getClusterName())) {
+          return true;
+        }
+      }
+    } finally {
+      r.unlock();
+    }
+
+    return false;
+  }
+
   @Override
   public Host getHostById(Long hostId) throws AmbariException {
     checkLoaded();
@@ -860,8 +884,9 @@ public class ClustersImpl implements Clusters {
       // This will also remove from kerberos_principal_hosts, hostconfigmapping, and configgrouphostmapping
       Set<Cluster> clusters = hostClusterMap.get(hostname);
       Set<Long> clusterIds = Sets.newHashSet();
-      for (Cluster cluster: clusters)
+      for (Cluster cluster: clusters) {
         clusterIds.add(cluster.getClusterId());
+      }
 
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef30868b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
----------------------------------------------------------------------
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 136a756..775fe83 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
@@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
 import java.util.List;
 import java.util.UUID;
 
+import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
+import org.apache.ambari.server.controller.RootServiceResponseFactory.Services;
 import org.apache.ambari.server.events.AlertReceivedEvent;
 import org.apache.ambari.server.events.listeners.alerts.AlertReceivedListener;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
@@ -306,4 +308,124 @@ public class AlertReceivedListenerTest {
     current = allCurrent.get(0);
     assertEquals(MaintenanceState.ON, current.getMaintenanceState());
   }
+
+  /**
+   * Tests that an invalid host from a host-level agent alert is rejected.
+   */
+  @Test
+  public void testAgentAlertFromInvalidHost() {
+    String definitionName = ALERT_DEFINITION + "1";
+    String serviceName = Services.AMBARI.name();
+    String componentName = Components.AMBARI_AGENT.name();
+
+    Alert alert1 = new Alert(definitionName, null, serviceName, componentName, HOST1,
+        AlertState.OK);
+
+    alert1.setCluster(m_cluster.getClusterName());
+    alert1.setLabel(ALERT_LABEL);
+    alert1.setText(serviceName + " " + componentName + " is OK");
+    alert1.setTimestamp(1L);
+
+    // verify that the listener works with a regular alert
+    AlertReceivedListener listener = m_injector.getInstance(AlertReceivedListener.class);
+    AlertReceivedEvent event1 = new AlertReceivedEvent(m_cluster.getClusterId(), alert1);
+    listener.onAlertEvent(event1);
+
+    List<AlertCurrentEntity> allCurrent = m_dao.findCurrent();
+    assertEquals(1, allCurrent.size());
+
+    // invalid host
+    alert1.setHostName("INVALID");
+
+    // remove all
+    m_dao.removeCurrentByHost(HOST1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(0, allCurrent.size());
+
+    // verify no new alerts received
+    listener.onAlertEvent(event1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(0, allCurrent.size());
+  }
+
+  /**
+   * Tests that an alert for AMBARI/AMBARI_SERVER is always valid.
+   */
+  @Test
+  public void testAmbariServerValidAlerts() {
+    String definitionName = ALERT_DEFINITION + "1";
+    String serviceName = Services.AMBARI.name();
+    String componentName = Components.AMBARI_SERVER.name();
+
+    Alert alert1 = new Alert(definitionName, null, serviceName, componentName, HOST1,
+        AlertState.OK);
+
+    alert1.setCluster(m_cluster.getClusterName());
+    alert1.setLabel(ALERT_LABEL);
+    alert1.setText(serviceName + " " + componentName + " is OK");
+    alert1.setTimestamp(1L);
+
+    // verify that the listener works with a regular alert
+    AlertReceivedListener listener = m_injector.getInstance(AlertReceivedListener.class);
+    AlertReceivedEvent event1 = new AlertReceivedEvent(m_cluster.getClusterId(), alert1);
+    listener.onAlertEvent(event1);
+
+    List<AlertCurrentEntity> allCurrent = m_dao.findCurrent();
+    assertEquals(1, allCurrent.size());
+
+    // invalid host, invalid cluster
+    alert1.setHostName("INVALID");
+    alert1.setCluster("INVALID");
+
+    // remove all
+    m_dao.removeCurrentByHost(HOST1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(0, allCurrent.size());
+
+    // verify that the alert was still received
+    listener.onAlertEvent(event1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(1, allCurrent.size());
+  }
+
+  /**
+   * Tests that an invalid host from an invalid cluster does not trigger an
+   * alert.
+   */
+  @Test
+  public void testMissingClusterAndInvalidHost() {
+    String definitionName = ALERT_DEFINITION + "1";
+    String serviceName = Services.AMBARI.name();
+    String componentName = Components.AMBARI_AGENT.name();
+
+    Alert alert1 = new Alert(definitionName, null, serviceName, componentName, HOST1,
+        AlertState.OK);
+
+    alert1.setCluster(m_cluster.getClusterName());
+    alert1.setLabel(ALERT_LABEL);
+    alert1.setText(serviceName + " " + componentName + " is OK");
+    alert1.setTimestamp(1L);
+
+    // verify that the listener works with a regular alert
+    AlertReceivedListener listener = m_injector.getInstance(AlertReceivedListener.class);
+    AlertReceivedEvent event1 = new AlertReceivedEvent(m_cluster.getClusterId(), alert1);
+    listener.onAlertEvent(event1);
+
+    List<AlertCurrentEntity> allCurrent = m_dao.findCurrent();
+    assertEquals(1, allCurrent.size());
+
+    // missing cluster, invalid host
+    alert1.setCluster(null);
+    alert1.setHostName("INVALID");
+
+    // remove all
+    m_dao.removeCurrentByHost(HOST1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(0, allCurrent.size());
+
+    // verify no new alerts received
+    listener.onAlertEvent(event1);
+    allCurrent = m_dao.findCurrent();
+    assertEquals(0, allCurrent.size());
+  }
 }


Mime
View raw message