You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/04/26 18:01:39 UTC

[33/34] ambari git commit: AMBARI-20851 - Provide Alert For Component OUT_OF_SYNC Issues (jonathanhurley)

AMBARI-20851 - Provide Alert For Component OUT_OF_SYNC Issues (jonathanhurley)


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

Branch: refs/heads/branch-feature-AMBARI-12556
Commit: 6a0b2a0e2be6901b13db0787fa70ff98905b19da
Parents: bd1b2c9
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Tue Apr 25 18:56:02 2017 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Wed Apr 26 13:21:01 2017 -0400

----------------------------------------------------------------------
 .../ambari/server/alerts/AlertRunnable.java     |  27 ++
 .../alerts/ComponentVersionAlertRunnable.java   | 195 ++++++++++
 ambari-server/src/main/resources/alerts.json    |  12 +
 .../ComponentVersionAlertRunnableTest.java      | 362 +++++++++++++++++++
 .../server/api/services/AmbariMetaInfoTest.java |  14 +-
 .../metadata/AgentAlertDefinitionsTest.java     |   2 +-
 6 files changed, 604 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/main/java/org/apache/ambari/server/alerts/AlertRunnable.java
----------------------------------------------------------------------
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 ea583e4..057a273 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
@@ -27,6 +27,7 @@ import org.apache.ambari.server.events.publishers.AlertEventPublisher;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 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;
@@ -162,4 +163,30 @@ public abstract class AlertRunnable implements Runnable {
     Number number = NumberUtils.createNumber((String) value);
     return number.intValue();
   }
+
+  /**
+   * Builds an {@link Alert} instance.
+   *
+   * @param cluster
+   *          the cluster the alert is for (not {@code null}).
+   * @param myDefinition
+   *          the alert's definition (not {@code null}).
+   * @param alertState
+   *          the state of the alert (not {@code null}).
+   * @param message
+   *          the alert text.
+   * @return and alert.
+   */
+  protected Alert buildAlert(Cluster cluster, AlertDefinitionEntity myDefinition,
+      AlertState alertState, String message) {
+    Alert alert = new Alert(myDefinition.getDefinitionName(), null, myDefinition.getServiceName(),
+        myDefinition.getComponentName(), null, alertState);
+
+    alert.setLabel(myDefinition.getLabel());
+    alert.setText(message);
+    alert.setTimestamp(System.currentTimeMillis());
+    alert.setCluster(cluster.getClusterName());
+
+    return alert;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/main/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnable.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnable.java b/ambari-server/src/main/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnable.java
new file mode 100644
index 0000000..7dfbe47
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnable.java
@@ -0,0 +1,195 @@
+/**
+ * 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 java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.UpgradeEntity;
+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.ComponentInfo;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.State;
+import org.apache.commons.lang.StringUtils;
+
+import com.google.inject.Inject;
+
+/**
+ * The {@link ComponentVersionAlertRunnable} is used to determine if the
+ * reported version of host components match what is expected. If there is a
+ * mismatch, then an alert will be triggered indicating which components are in
+ * need of attention.
+ * <p/>
+ * This alert will not run during upgrades or when the cluster is still being
+ * provisioned.
+ */
+public class ComponentVersionAlertRunnable extends AlertRunnable {
+
+  /**
+   * The message for the alert when all components are reporting correct
+   * versions.
+   */
+  private static final String ALL_COMPONENTS_CORRECT_MSG = "All components are reporting their expected versions.";
+
+  /**
+   * The message for the alert when there is an upgrade in progress.
+   */
+  private static final String UPGRADE_IN_PROGRESS_MSG = "This alert will be suspended while the upgrade to {0} is in progress.";
+
+  /**
+   * The unknown component error message.
+   */
+  private static final String UNKNOWN_COMPONENT_MSG_TEMPLATE = "Unable to retrieve component information for {0}/{1}";
+
+  /**
+   * The version mismatch message.
+   */
+  private static final String MISMATCHED_VERSIONS_MSG = "The following components are reporting unexpected versions: ";
+
+  /**
+   * The message when there is no CURRENT cluster version, but the cluster is
+   * still being setup.
+   */
+  private static final String CLUSTER_PROVISIONING_MSG = "The cluster is currently being provisioned. This alert will be skipped.";
+
+  /**
+   * The message when there is no CURRENT cluster version.
+   */
+  private static final String CLUSTER_OUT_OF_SYNC_MSG = "The cluster's CURRENT version could not be determined.";
+
+  @Inject
+  private AmbariMetaInfo m_metaInfo;
+
+  /**
+   * Constructor.
+   *
+   * @param definitionName
+   */
+  public ComponentVersionAlertRunnable(String definitionName) {
+    super(definitionName);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  List<Alert> execute(Cluster cluster, AlertDefinitionEntity myDefinition) {
+    // if there is an upgrade in progress, then skip running this alert
+    UpgradeEntity upgrade = cluster.getUpgradeInProgress();
+    if (null != upgrade) {
+      String message = MessageFormat.format(UPGRADE_IN_PROGRESS_MSG, upgrade.getToVersion());
+
+      return Collections.singletonList(
+          buildAlert(cluster, myDefinition, AlertState.SKIPPED, message));
+    }
+
+    TreeMap<Host, Set<ServiceComponentHost>> versionMismatches = new TreeMap<>();
+    Collection<Host> hosts = cluster.getHosts();
+
+    // no cluster version is very bad ...
+    ClusterVersionEntity clusterVersionEntity = cluster.getCurrentClusterVersion();
+    if (null == clusterVersionEntity) {
+      if (cluster.getProvisioningState() == State.INIT
+          || cluster.getAllClusterVersions().size() == 1) {
+        return Collections.singletonList(
+            buildAlert(cluster, myDefinition, AlertState.SKIPPED, CLUSTER_PROVISIONING_MSG));
+      } else {
+        return Collections.singletonList(
+            buildAlert(cluster, myDefinition, AlertState.CRITICAL, CLUSTER_OUT_OF_SYNC_MSG));
+      }
+    }
+
+    RepositoryVersionEntity repositoryVersionEntity = clusterVersionEntity.getRepositoryVersion();
+    String clusterVersion = repositoryVersionEntity.getVersion();
+
+    for (Host host : hosts) {
+      List<ServiceComponentHost> hostComponents = cluster.getServiceComponentHosts(
+          host.getHostName());
+      for (ServiceComponentHost hostComponent : hostComponents) {
+        StackId desiredStackId = hostComponent.getDesiredStackVersion();
+
+        final ComponentInfo componentInfo;
+        try {
+          componentInfo = m_metaInfo.getComponent(desiredStackId.getStackName(),
+              desiredStackId.getStackVersion(), hostComponent.getServiceName(),
+              hostComponent.getServiceComponentName());
+        } catch (AmbariException ambariException) {
+          // throw an UNKNOWN response if we can't load component info
+          String message = MessageFormat.format(UNKNOWN_COMPONENT_MSG_TEMPLATE,
+              hostComponent.getServiceName(), hostComponent.getServiceComponentName());
+
+          return Collections.singletonList(
+              buildAlert(cluster, myDefinition, AlertState.UNKNOWN, message));
+        }
+
+        // skip components that don't advertise a version
+        if (!componentInfo.isVersionAdvertised()) {
+          continue;
+        }
+
+        String version = hostComponent.getVersion();
+        if (!StringUtils.equals(version, clusterVersion)) {
+          Set<ServiceComponentHost> mismatchedComponents = versionMismatches.get(host);
+          if (null == mismatchedComponents) {
+            mismatchedComponents = new HashSet<>();
+            versionMismatches.put(host, mismatchedComponents);
+          }
+
+          mismatchedComponents.add(hostComponent);
+        }
+      }
+    }
+
+    AlertState alertState = AlertState.OK;
+    String alertText = ALL_COMPONENTS_CORRECT_MSG;
+
+    // if there are any components reporting the wrong version, fire off a warning
+    if (!versionMismatches.isEmpty()) {
+      StringBuilder buffer = new StringBuilder(MISMATCHED_VERSIONS_MSG);
+      buffer.append(System.lineSeparator());
+
+      for (Host host : versionMismatches.keySet()) {
+        buffer.append("  ").append(host.getHostName());
+        buffer.append(System.lineSeparator());
+        for (ServiceComponentHost hostComponent : versionMismatches.get(host)) {
+          buffer.append("    ").append(hostComponent.getServiceComponentName()).append(": ").append(
+              hostComponent.getVersion()).append(System.lineSeparator());
+        }
+      }
+
+      alertText = buffer.toString();
+      alertState = AlertState.WARNING;
+    }
+
+    return Collections.singletonList(buildAlert(cluster, myDefinition, alertState, alertText));
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/main/resources/alerts.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/alerts.json b/ambari-server/src/main/resources/alerts.json
index d646401..732aae0 100644
--- a/ambari-server/src/main/resources/alerts.json
+++ b/ambari-server/src/main/resources/alerts.json
@@ -106,6 +106,18 @@
             }
           ]
         }
+      },
+      {
+        "name": "ambari_server_component_version",
+        "label": "Component Version",
+        "description": "This alert is triggered if the server detects that there is a problem with the expected and reported version of a component. The alert is suppressed automatically during an upgrade.",
+        "interval": 5,
+        "scope": "SERVICE",
+        "enabled": true,
+        "source": {
+          "type": "SERVER",
+          "class": "org.apache.ambari.server.alerts.ComponentVersionAlertRunnable"
+        }
       }
     ],
     "AMBARI_AGENT" : [

http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/test/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnableTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnableTest.java b/ambari-server/src/test/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnableTest.java
new file mode 100644
index 0000000..98f6f44
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/alerts/ComponentVersionAlertRunnableTest.java
@@ -0,0 +1,362 @@
+/**
+ * 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 junit.framework.Assert.assertEquals;
+import static org.easymock.EasyMock.expect;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.events.AlertEvent;
+import org.apache.ambari.server.events.AlertReceivedEvent;
+import org.apache.ambari.server.events.MockEventListener;
+import org.apache.ambari.server.events.publishers.AlertEventPublisher;
+import org.apache.ambari.server.orm.DBAccessor;
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.UpgradeEntity;
+import org.apache.ambari.server.stack.StackManagerFactory;
+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;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.easymock.EasyMock;
+import org.easymock.EasyMockSupport;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.eventbus.EventBus;
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Tests {@link ComponentVersionAlertRunnable}.
+ */
+public class ComponentVersionAlertRunnableTest extends EasyMockSupport {
+
+  private final static long CLUSTER_ID = 1;
+  private final static String CLUSTER_NAME = "c1";
+  private final static String HOSTNAME_1 = "c6401.ambari.apache.org";
+  private final static String HOSTNAME_2 = "c6402.ambari.apache.org";
+
+  private final static String EXPECTED_VERSION = "2.6.0.0-1234";
+  private final static String WRONG_VERSION = "9.9.9.9-9999";
+
+  private final static String DEFINITION_NAME = "ambari_server_component_version";
+  private final static String DEFINITION_SERVICE = "AMBARI";
+  private final static String DEFINITION_COMPONENT = "AMBARI_SERVER";
+  private final static String DEFINITION_LABEL = "Mock Definition";
+
+  private Clusters m_clusters;
+  private Cluster m_cluster;
+  private Injector m_injector;
+  private AlertDefinitionDAO m_definitionDao;
+  private AlertDefinitionEntity m_definition;
+  private MockEventListener m_listener;
+  private AmbariMetaInfo m_metaInfo;
+
+  private AlertEventPublisher m_eventPublisher;
+  private EventBus m_synchronizedBus;
+
+  private Collection<Host> m_hosts;
+  private Map<String, List<ServiceComponentHost>> m_hostComponentMap = new HashMap<>();
+  private StackId m_desidredStackId;
+
+  /**
+   *
+   */
+  @Before
+  public void setup() throws Exception {
+    m_injector = Guice.createInjector(new MockModule());
+    m_definitionDao = m_injector.getInstance(AlertDefinitionDAO.class);
+    m_clusters = m_injector.getInstance(Clusters.class);
+    m_cluster = m_injector.getInstance(Cluster.class);
+    m_eventPublisher = m_injector.getInstance(AlertEventPublisher.class);
+    m_listener = m_injector.getInstance(MockEventListener.class);
+    m_definition = createNiceMock(AlertDefinitionEntity.class);
+    m_metaInfo = m_injector.getInstance(AmbariMetaInfo.class);
+
+    // !!! need a synchronous op for testing
+    m_synchronizedBus = new EventBus();
+    Field field = AlertEventPublisher.class.getDeclaredField("m_eventBus");
+    field.setAccessible(true);
+    field.set(m_eventPublisher, m_synchronizedBus);
+
+    // register mock listener
+    m_synchronizedBus.register(m_listener);
+
+    // create the cluster map
+    Map<String,Cluster> clusterMap = new HashMap<>();
+    clusterMap.put(CLUSTER_NAME, m_cluster);
+
+    // hosts
+    m_hosts = new ArrayList<>();
+    Host host1 = createNiceMock(Host.class);
+    Host host2 = createNiceMock(Host.class);
+    expect(host1.getHostName()).andReturn(HOSTNAME_1).atLeastOnce();
+    expect(host2.getHostName()).andReturn(HOSTNAME_2).atLeastOnce();
+    m_hosts.add(host1);
+    m_hosts.add(host2);
+
+    m_hostComponentMap.put(HOSTNAME_1, new ArrayList<ServiceComponentHost>());
+    m_hostComponentMap.put(HOSTNAME_2, new ArrayList<ServiceComponentHost>());
+
+    // desired stack
+    m_desidredStackId = createNiceMock(StackId.class);
+    expect(m_desidredStackId.getStackName()).andReturn("SOME-STACK").atLeastOnce();
+    expect(m_desidredStackId.getStackVersion()).andReturn("STACK-VERSION").atLeastOnce();
+
+    // components
+    ServiceComponentHost sch1_1 = createNiceMock(ServiceComponentHost.class);
+    ServiceComponentHost sch1_2 = createNiceMock(ServiceComponentHost.class);
+    ServiceComponentHost sch2_1 = createNiceMock(ServiceComponentHost.class);
+    ServiceComponentHost sch2_2 = createNiceMock(ServiceComponentHost.class);
+
+    expect(sch1_1.getServiceName()).andReturn("FOO").atLeastOnce();
+    expect(sch1_1.getServiceComponentName()).andReturn("FOO_COMPONENT").atLeastOnce();
+    expect(sch1_1.getVersion()).andReturn(EXPECTED_VERSION).atLeastOnce();
+    expect(sch1_1.getDesiredStackVersion()).andReturn(m_desidredStackId).atLeastOnce();
+    expect(sch1_2.getServiceName()).andReturn("BAR").atLeastOnce();
+    expect(sch1_2.getServiceComponentName()).andReturn("BAR_COMPONENT").atLeastOnce();
+    expect(sch1_2.getVersion()).andReturn(EXPECTED_VERSION).atLeastOnce();
+    expect(sch1_2.getDesiredStackVersion()).andReturn(m_desidredStackId).atLeastOnce();
+    expect(sch2_1.getServiceName()).andReturn("FOO").atLeastOnce();
+    expect(sch2_1.getServiceComponentName()).andReturn("FOO_COMPONENT").atLeastOnce();
+    expect(sch2_1.getVersion()).andReturn(EXPECTED_VERSION).atLeastOnce();
+    expect(sch2_1.getDesiredStackVersion()).andReturn(m_desidredStackId).atLeastOnce();
+    expect(sch2_2.getServiceName()).andReturn("BAZ").atLeastOnce();
+    expect(sch2_2.getServiceComponentName()).andReturn("BAZ_COMPONENT").atLeastOnce();
+    expect(sch2_2.getVersion()).andReturn(EXPECTED_VERSION).atLeastOnce();
+    expect(sch2_2.getDesiredStackVersion()).andReturn(m_desidredStackId).atLeastOnce();
+
+    m_hostComponentMap.get(HOSTNAME_1).add(sch1_1);
+    m_hostComponentMap.get(HOSTNAME_1).add(sch1_2);
+    m_hostComponentMap.get(HOSTNAME_2).add(sch2_1);
+    m_hostComponentMap.get(HOSTNAME_2).add(sch2_2);
+
+    // mock the definition for the alert
+    expect(m_definition.getDefinitionName()).andReturn(DEFINITION_NAME).atLeastOnce();
+    expect(m_definition.getServiceName()).andReturn(DEFINITION_SERVICE).atLeastOnce();
+    expect(m_definition.getComponentName()).andReturn(DEFINITION_COMPONENT).atLeastOnce();
+    expect(m_definition.getLabel()).andReturn(DEFINITION_LABEL).atLeastOnce();
+    expect(m_definition.getEnabled()).andReturn(true).atLeastOnce();
+
+    // mock the cluster
+    expect(m_cluster.getClusterId()).andReturn(CLUSTER_ID).atLeastOnce();
+    expect(m_cluster.getClusterName()).andReturn(CLUSTER_NAME).atLeastOnce();
+    expect(m_cluster.getHosts()).andReturn(m_hosts).atLeastOnce();
+
+    ClusterVersionEntity clusterVersionEntity = createNiceMock(ClusterVersionEntity.class);
+    RepositoryVersionEntity repositoryVersionEntity = createNiceMock(RepositoryVersionEntity.class);
+    expect(clusterVersionEntity.getRepositoryVersion()).andReturn(
+        repositoryVersionEntity).anyTimes();
+
+    expect(repositoryVersionEntity.getVersion()).andReturn(EXPECTED_VERSION).anyTimes();
+    expect(m_cluster.getCurrentClusterVersion()).andReturn(clusterVersionEntity).anyTimes();
+
+    // mock clusters
+    expect(m_clusters.getClusters()).andReturn(clusterMap).atLeastOnce();
+
+    // mock the definition DAO
+    expect(m_definitionDao.findByName(CLUSTER_ID, DEFINITION_NAME)).andReturn(
+        m_definition).atLeastOnce();
+
+    m_metaInfo.init();
+    EasyMock.expectLastCall().anyTimes();
+
+    // expect the cluster host mapping
+    expect(m_cluster.getServiceComponentHosts(HOSTNAME_1)).andReturn(
+        m_hostComponentMap.get(HOSTNAME_1)).once();
+    expect(m_cluster.getServiceComponentHosts(HOSTNAME_2)).andReturn(
+        m_hostComponentMap.get(HOSTNAME_2)).once();
+
+    // expect the component from metainfo
+    ComponentInfo componentInfo = createNiceMock(ComponentInfo.class);
+    expect(componentInfo.isVersionAdvertised()).andReturn(true).atLeastOnce();
+    expect(m_metaInfo.getComponent(EasyMock.anyString(), EasyMock.anyString(), EasyMock.anyString(),
+        EasyMock.anyString())).andReturn(componentInfo).atLeastOnce();
+    }
+
+  /**
+   * @throws Exception
+   */
+  @After
+  public void teardown() throws Exception {
+  }
+
+  /**
+   * Tests that the alert is SKIPPED when there is an upgrade in progress.
+   */
+  @Test
+  public void testUpgradeInProgress() throws Exception {
+    UpgradeEntity upgrade = createNiceMock(UpgradeEntity.class);
+    expect(upgrade.getToVersion()).andReturn("VERSION").once();
+    expect(m_cluster.getUpgradeInProgress()).andReturn(upgrade).once();
+
+    replayAll();
+
+    m_metaInfo.init();
+
+    // precondition that no events were fired
+    assertEquals(0, m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+
+    // instantiate and inject mocks
+    ComponentVersionAlertRunnable runnable = new ComponentVersionAlertRunnable(
+        m_definition.getDefinitionName());
+
+    m_injector.injectMembers(runnable);
+
+    // 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.SKIPPED, alert.getState());
+    assertEquals(DEFINITION_NAME, alert.getName());
+  }
+
+  /**
+   * Tests the alert that fires when all components are reporting correct
+   * versions.
+   */
+  @Test
+  public void testAllComponentVersionsCorrect() throws Exception {
+    replayAll();
+
+    m_metaInfo.init();
+
+    // precondition that no events were fired
+    assertEquals(0, m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+
+    // instantiate and inject mocks
+    ComponentVersionAlertRunnable runnable = new ComponentVersionAlertRunnable(
+        m_definition.getDefinitionName());
+
+    m_injector.injectMembers(runnable);
+
+    // 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());
+
+    verifyAll();
+  }
+
+  /**
+   * Tests that the alert which fires when there is a mismatch is a WARNING.
+   */
+  @Test
+  public void testomponentVersionMismatch() throws Exception {
+    // reset expectation so that it returns a wrong version
+    ServiceComponentHost sch = m_hostComponentMap.get(HOSTNAME_1).get(0);
+    EasyMock.reset(sch);
+    expect(sch.getServiceName()).andReturn("FOO").atLeastOnce();
+    expect(sch.getServiceComponentName()).andReturn("FOO_COMPONENT").atLeastOnce();
+    expect(sch.getVersion()).andReturn(WRONG_VERSION).atLeastOnce();
+    expect(sch.getDesiredStackVersion()).andReturn(m_desidredStackId).atLeastOnce();
+
+    replayAll();
+
+    m_metaInfo.init();
+
+    // precondition that no events were fired
+    assertEquals(0, m_listener.getAlertEventReceivedCount(AlertReceivedEvent.class));
+
+    // instantiate and inject mocks
+    ComponentVersionAlertRunnable runnable = new ComponentVersionAlertRunnable(
+        m_definition.getDefinitionName());
+
+    m_injector.injectMembers(runnable);
+
+    // 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.WARNING, alert.getState());
+    assertEquals(DEFINITION_NAME, alert.getName());
+
+    verifyAll();
+  }
+
+
+  /**
+   *
+   */
+  private class MockModule implements Module {
+    /**
+     *
+     */
+    @Override
+    public void configure(Binder binder) {
+      Cluster cluster = createNiceMock(Cluster.class);
+
+      binder.bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
+      binder.bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
+      binder.bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class));
+      binder.bind(Cluster.class).toInstance(cluster);
+      binder.bind(AlertDefinitionDAO.class).toInstance(createNiceMock(AlertDefinitionDAO.class));
+      binder.bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
+      binder.bind(AmbariMetaInfo.class).toInstance(createNiceMock(AmbariMetaInfo.class));
+      binder.bind(StackManagerFactory.class).toInstance(createNiceMock(StackManagerFactory.class));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
index 884777d..c9acfe9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
@@ -1942,7 +1942,7 @@ public class AmbariMetaInfoTest {
 
     AlertDefinitionDAO dao = injector.getInstance(AlertDefinitionDAO.class);
     List<AlertDefinitionEntity> definitions = dao.findAll(clusterId);
-    assertEquals(12, definitions.size());
+    assertEquals(13, definitions.size());
 
     // figure out how many of these alerts were merged into from the
     // non-stack alerts.json
@@ -1955,7 +1955,7 @@ public class AmbariMetaInfoTest {
     }
 
     assertEquals(3, hostAlertCount);
-    assertEquals(9, definitions.size() - hostAlertCount);
+    assertEquals(10, definitions.size() - hostAlertCount);
 
     for (AlertDefinitionEntity definition : definitions) {
       definition.setScheduleInterval(28);
@@ -1965,7 +1965,7 @@ public class AmbariMetaInfoTest {
     metaInfo.reconcileAlertDefinitions(clusters);
 
     definitions = dao.findAll();
-    assertEquals(12, definitions.size());
+    assertEquals(13, definitions.size());
 
     for (AlertDefinitionEntity definition : definitions) {
       assertEquals(28, definition.getScheduleInterval().intValue());
@@ -1974,7 +1974,7 @@ public class AmbariMetaInfoTest {
     // find all enabled for the cluster should find 6 (the ones from HDFS;
     // it will not find the agent alert since it's not bound to the cluster)
     definitions = dao.findAllEnabled(cluster.getClusterId());
-    assertEquals(11, definitions.size());
+    assertEquals(12, definitions.size());
 
     // create new definition
     AlertDefinitionEntity entity = new AlertDefinitionEntity();
@@ -1993,19 +1993,19 @@ public class AmbariMetaInfoTest {
 
     // verify the new definition is found (6 HDFS + 1 new one)
     definitions = dao.findAllEnabled(cluster.getClusterId());
-    assertEquals(12, definitions.size());
+    assertEquals(13, definitions.size());
 
     // reconcile, which should disable our bad definition
     metaInfo.reconcileAlertDefinitions(clusters);
 
     // find all enabled for the cluster should find 6
     definitions = dao.findAllEnabled(cluster.getClusterId());
-    assertEquals(11, definitions.size());
+    assertEquals(12, definitions.size());
 
     // find all should find 6 HDFS + 1 disabled + 1 agent alert + 2 server
     // alerts
     definitions = dao.findAll();
-    assertEquals(13, definitions.size());
+    assertEquals(14, definitions.size());
 
     entity = dao.findById(entity.getDefinitionId());
     assertFalse(entity.getEnabled());

http://git-wip-us.apache.org/repos/asf/ambari/blob/6a0b2a0e/ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java
index 7378b8c..e893503f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/metadata/AgentAlertDefinitionsTest.java
@@ -77,7 +77,7 @@ public class AgentAlertDefinitionsTest {
   public void testLoadingServertAlerts() {
     AmbariServiceAlertDefinitions ambariServiceAlertDefinitions = m_injector.getInstance(AmbariServiceAlertDefinitions.class);
     List<AlertDefinition> definitions = ambariServiceAlertDefinitions.getServerDefinitions();
-    Assert.assertEquals(3, definitions.size());
+    Assert.assertEquals(4, definitions.size());
 
     for (AlertDefinition definition : definitions) {
       Assert.assertEquals(Components.AMBARI_SERVER.name(),