You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by dm...@apache.org on 2016/03/11 16:46:02 UTC

[1/2] ambari git commit: AMBARI-15374. Check To Ensure That All Components Are On The Same Version Before Upgrading (dlysnichenko)

Repository: ambari
Updated Branches:
  refs/heads/branch-2.2 3b5a96d27 -> b400a0510
  refs/heads/trunk 962a97304 -> 7eba6fd39


AMBARI-15374. Check To Ensure That All Components Are On The Same Version Before Upgrading (dlysnichenko)


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

Branch: refs/heads/branch-2.2
Commit: b400a051051a7f621e22b0048e91b7d6d8f9105e
Parents: 3b5a96d
Author: Lisnichenko Dmitro <dl...@hortonworks.com>
Authored: Fri Mar 11 17:44:26 2016 +0200
Committer: Lisnichenko Dmitro <dl...@hortonworks.com>
Committed: Fri Mar 11 17:44:26 2016 +0200

----------------------------------------------------------------------
 .../ambari/server/checks/CheckDescription.java  |   7 +
 .../ServiceComponentHostVersionMatchCheck.java  | 189 +++++++++++++++++++
 .../ambari/server/checks/UpgradeCheckGroup.java |   5 +
 ...rviceComponentHostVersionMatchCheckTest.java | 128 +++++++++++++
 4 files changed, 329 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b400a051/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java
index ba987aa..b06848a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDescription.java
@@ -224,6 +224,13 @@ public enum CheckDescription {
           " That is a potential problem when doing stack update.");
       }}),
 
+  VERSION_MISMATCH(PrereqCheckType.HOST,
+      "All components must be reporting the expected version",
+      new HashMap<String, String>() {{
+        put(AbstractCheckDescriptor.DEFAULT,
+            "There are components which are not reporting the expected stack version: \n%s");
+      }}),
+
   SERVICES_RANGER_PASSWORD_VERIFY(PrereqCheckType.SERVICE,
       "Verify Ambari and Ranger Password Synchronization",
       new HashMap<String, String>() {{

http://git-wip-us.apache.org/repos/asf/ambari/blob/b400a051/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
new file mode 100644
index 0000000..37e591b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
@@ -0,0 +1,189 @@
+/**
+ * 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.checks;
+
+import com.google.inject.Singleton;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.PrereqCheckRequest;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.stack.PrereqCheckStatus;
+import org.apache.ambari.server.state.stack.PrerequisiteCheck;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Warns about host components whose upgrade state is VERSION_MISMATCH. Never triggers
+ * fail.
+ */
+@Singleton
+@UpgradeCheck(group = UpgradeCheckGroup.COMPONENT_VERSION, order = 7.0f, required = true)
+public class ServiceComponentHostVersionMatchCheck extends AbstractCheckDescriptor {
+  public ServiceComponentHostVersionMatchCheck() {
+    super(CheckDescription.VERSION_MISMATCH);
+  }
+
+  @Override
+  public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException {
+    final String clusterName = request.getClusterName();
+    final Cluster cluster = clustersProvider.get().getCluster(clusterName);
+    RepositoryVersionEntity clusterVersion = cluster.getCurrentClusterVersion().getRepositoryVersion();
+    List<String> errorMessages = new ArrayList<String>();
+    Map<String, Service> services = cluster.getServices();
+
+    // If CURRENT cluster version is already computed
+    if (clusterVersion != null) {
+      String desiredVersion = clusterVersion.getVersion();
+      for (Service service : services.values()) {
+        validateService(service, desiredVersion, prerequisiteCheck, errorMessages);
+      }
+    } else {
+      listAllComponentsWithHostVersions(services.values(), prerequisiteCheck, errorMessages);
+    }
+
+    if (!prerequisiteCheck.getFailedOn().isEmpty()) {
+      prerequisiteCheck.setStatus(PrereqCheckStatus.WARNING);
+      String failReason = getFailReason(prerequisiteCheck, request);
+      prerequisiteCheck.setFailReason(String.format(failReason, StringUtils.join(errorMessages, "\n")));
+    }
+  }
+
+  /**
+   * Iterates over all service components belonging to a service and validates them.
+   * @param service
+   * @param desiredVersion current version for cluster. All service component versions
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateService(Service service,
+                               String desiredVersion,
+                               PrerequisiteCheck prerequisiteCheck,
+                               List<String> errorMessages) {
+    Map<String, ServiceComponent> serviceComponents = service.getServiceComponents();
+    for (ServiceComponent serviceComponent : serviceComponents.values()) {
+      validateServiceComponent(serviceComponent, desiredVersion, prerequisiteCheck, errorMessages);
+    }
+  }
+
+  /**
+   * Iterates over all host components belonging to a service component and validates them.
+   * @param serviceComponent
+   * @param desiredVersion current version for cluster. All host component versions
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateServiceComponent(ServiceComponent serviceComponent,
+                                        String desiredVersion,
+                                        PrerequisiteCheck prerequisiteCheck,
+                                        List<String> errorMessages) {
+    if (!serviceComponent.isVersionAdvertised())
+      return;
+
+    Map<String, ServiceComponentHost> serviceComponentHosts = serviceComponent.getServiceComponentHosts();
+    for (ServiceComponentHost serviceComponentHost : serviceComponentHosts.values()) {
+      validateServiceComponentHost(serviceComponentHost, desiredVersion, prerequisiteCheck, errorMessages);
+    }
+  }
+
+  /**
+   * Validates host component. If upgrade state of host component is VERSION_MISMATCH,
+   * adds hostname to a Failed On map of prerequisite check, and adds all other
+   * host component version details to errorMessages
+   * @param serviceComponentHost
+   * @param desiredVersion current version for cluster. Component host version
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateServiceComponentHost(ServiceComponentHost serviceComponentHost,
+                                            String desiredVersion,
+                                            PrerequisiteCheck prerequisiteCheck,
+                                            List<String> errorMessages) {
+    String actualVersion = serviceComponentHost.getVersion();
+    if (StringUtils.equals(desiredVersion, actualVersion))
+      return;
+
+    String hostName = serviceComponentHost.getHostName();
+    String serviceComponentName = serviceComponentHost.getServiceComponentName();
+
+    String message = hostName + "/" + serviceComponentName
+        + " desired version: " + desiredVersion
+        + ", actual version: " + actualVersion;
+    prerequisiteCheck.getFailedOn().add(hostName);
+    errorMessages.add(message);
+  }
+
+  /**
+   * Validates component version in a cluster that has no CURRENT version defined.
+   * If there is more than one actual host component version in cluster, collects
+   * details for warning.
+   * @param services
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void listAllComponentsWithHostVersions(Collection<Service> services,
+                                                 PrerequisiteCheck prerequisiteCheck,
+                                                 List<String> errorMessages) {
+    if (countDistinctVersionsOnHosts(services) <= 1)
+      return;
+
+    for (Service service : services) {
+      for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
+        if (!serviceComponent.isVersionAdvertised())
+          continue;
+
+        ArrayList<Object> hostVersions = new ArrayList<>();
+        for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
+          hostVersions.add(serviceComponentHost.getVersion());
+          prerequisiteCheck.getFailedOn().add(serviceComponentHost.getHostName());
+        }
+        String message = serviceComponent.getName() + " host versions: " + hostVersions;
+        errorMessages.add(message);
+      }
+    }
+  }
+
+  /**
+   * Iterates over services, and enumerates reported versions of host components.
+   * @param services collection of services
+   * @return number of distinct actual versions found
+   */
+  private int countDistinctVersionsOnHosts(Collection<Service> services) {
+    HashSet<Object> versions = new HashSet<>();
+    for (Service service : services) {
+      for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
+        if (!serviceComponent.isVersionAdvertised())
+          continue;
+        for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
+          versions.add(serviceComponentHost.getVersion());
+        }
+      }
+    }
+    return versions.size();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b400a051/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckGroup.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckGroup.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckGroup.java
index 0be5be1..0b24426 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckGroup.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckGroup.java
@@ -69,6 +69,11 @@ public enum UpgradeCheckGroup {
    */
   CONFIGURATION_WARNING(8.0f),
 
+  /***
+   * Checks the component version on the hosts.
+   */
+  COMPONENT_VERSION(9.0f),
+
   /**
    * All other checks.
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/b400a051/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java b/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
new file mode 100644
index 0000000..f525b40
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
@@ -0,0 +1,128 @@
+/**
+ * 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.checks;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provider;
+import org.apache.ambari.server.controller.PrereqCheckRequest;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.stack.PrereqCheckStatus;
+import org.apache.ambari.server.state.stack.PrerequisiteCheck;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ServiceComponentHostVersionMatchCheckTest {
+  private static final String CLUSTER_NAME = "cluster1";
+  private static final String FIRST_SERVICE_NAME = "service1";
+  private static final String FIRST_SERVICE_COMPONENT_NAME = "component1";
+  private static final String FIRST_SERVICE_COMPONENT_HOST_NAME = "host1";
+  private static final String SECOND_SERVICE_COMPONENT_HOST_NAME = "host2";
+  private static final String CURRENT_VERSION = "1.1.1.1";
+  private static final String OTHER_VERSION = "1.2.3.4";
+  private ServiceComponentHostVersionMatchCheck versionMismatchCheck;
+  private Cluster cluster;
+  private ServiceComponentHost firstServiceComponentHost;
+  private ServiceComponentHost secondServiceComponentHost;
+
+  @Before
+  public void setUp() throws Exception {
+    final Clusters clusters = mock(Clusters.class);
+    versionMismatchCheck = new ServiceComponentHostVersionMatchCheck();
+    versionMismatchCheck.clustersProvider = new Provider<Clusters>() {
+      @Override
+      public Clusters get() {
+        return clusters;
+      }
+    };
+
+    cluster = mock(Cluster.class, RETURNS_DEEP_STUBS);
+    when(clusters.getCluster(CLUSTER_NAME)).thenReturn(cluster);
+
+    Service firstService = mock(Service.class);
+    Map<String, Service> services = ImmutableMap.of(FIRST_SERVICE_NAME, firstService);
+    when(cluster.getServices()).thenReturn(services);
+
+    ServiceComponent firstServiceComponent = mock(ServiceComponent.class);
+    Map<String, ServiceComponent> components = ImmutableMap.of(FIRST_SERVICE_COMPONENT_NAME, firstServiceComponent);
+    when(firstServiceComponent.isVersionAdvertised()).thenReturn(true);
+    when(firstService.getServiceComponents()).thenReturn(components);
+
+    firstServiceComponentHost = mock(ServiceComponentHost.class);
+    secondServiceComponentHost = mock(ServiceComponentHost.class);
+    Map<String, ServiceComponentHost> firstServiceComponentHosts = ImmutableMap.of(
+        FIRST_SERVICE_COMPONENT_HOST_NAME, firstServiceComponentHost,
+        SECOND_SERVICE_COMPONENT_HOST_NAME, secondServiceComponentHost
+    );
+    when(firstServiceComponent.getServiceComponentHosts()).thenReturn(firstServiceComponentHosts);
+  }
+
+  @Test
+  public void testWarningWhenHostWithVersionOtherThanCurrentClusterVersionExists() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion().getVersion()).thenReturn(CURRENT_VERSION);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(OTHER_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.WARNING, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenHostWithVersionOtherThanCurrentClusterVersionDoesNotExist() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion().getVersion()).thenReturn(CURRENT_VERSION);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenHostsWithDifferentVersionsExistAndCurrentClusterVersionIsUnknown() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion()).thenReturn(null);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(OTHER_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.WARNING, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenAllHostsHaveSameVersionAndCurrentClusterVersionIsUnknown() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion()).thenReturn(null);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+  }
+}
\ No newline at end of file


[2/2] ambari git commit: AMBARI-15374. Check To Ensure That All Components Are On The Same Version Before Upgrading (dlysnichenko)

Posted by dm...@apache.org.
AMBARI-15374. Check To Ensure That All Components Are On The Same Version Before Upgrading (dlysnichenko)


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

Branch: refs/heads/trunk
Commit: 7eba6fd39d84ddbf9a002d60651e7ebef1692e4f
Parents: 962a973
Author: Lisnichenko Dmitro <dl...@hortonworks.com>
Authored: Fri Mar 11 17:44:26 2016 +0200
Committer: Lisnichenko Dmitro <dl...@hortonworks.com>
Committed: Fri Mar 11 17:45:28 2016 +0200

----------------------------------------------------------------------
 .../ServiceComponentHostVersionMatchCheck.java  | 189 +++++++++++++++++++
 ...rviceComponentHostVersionMatchCheckTest.java | 128 +++++++++++++
 2 files changed, 317 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/7eba6fd3/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
new file mode 100644
index 0000000..37e591b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheck.java
@@ -0,0 +1,189 @@
+/**
+ * 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.checks;
+
+import com.google.inject.Singleton;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.PrereqCheckRequest;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.stack.PrereqCheckStatus;
+import org.apache.ambari.server.state.stack.PrerequisiteCheck;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Warns about host components whose upgrade state is VERSION_MISMATCH. Never triggers
+ * fail.
+ */
+@Singleton
+@UpgradeCheck(group = UpgradeCheckGroup.COMPONENT_VERSION, order = 7.0f, required = true)
+public class ServiceComponentHostVersionMatchCheck extends AbstractCheckDescriptor {
+  public ServiceComponentHostVersionMatchCheck() {
+    super(CheckDescription.VERSION_MISMATCH);
+  }
+
+  @Override
+  public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException {
+    final String clusterName = request.getClusterName();
+    final Cluster cluster = clustersProvider.get().getCluster(clusterName);
+    RepositoryVersionEntity clusterVersion = cluster.getCurrentClusterVersion().getRepositoryVersion();
+    List<String> errorMessages = new ArrayList<String>();
+    Map<String, Service> services = cluster.getServices();
+
+    // If CURRENT cluster version is already computed
+    if (clusterVersion != null) {
+      String desiredVersion = clusterVersion.getVersion();
+      for (Service service : services.values()) {
+        validateService(service, desiredVersion, prerequisiteCheck, errorMessages);
+      }
+    } else {
+      listAllComponentsWithHostVersions(services.values(), prerequisiteCheck, errorMessages);
+    }
+
+    if (!prerequisiteCheck.getFailedOn().isEmpty()) {
+      prerequisiteCheck.setStatus(PrereqCheckStatus.WARNING);
+      String failReason = getFailReason(prerequisiteCheck, request);
+      prerequisiteCheck.setFailReason(String.format(failReason, StringUtils.join(errorMessages, "\n")));
+    }
+  }
+
+  /**
+   * Iterates over all service components belonging to a service and validates them.
+   * @param service
+   * @param desiredVersion current version for cluster. All service component versions
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateService(Service service,
+                               String desiredVersion,
+                               PrerequisiteCheck prerequisiteCheck,
+                               List<String> errorMessages) {
+    Map<String, ServiceComponent> serviceComponents = service.getServiceComponents();
+    for (ServiceComponent serviceComponent : serviceComponents.values()) {
+      validateServiceComponent(serviceComponent, desiredVersion, prerequisiteCheck, errorMessages);
+    }
+  }
+
+  /**
+   * Iterates over all host components belonging to a service component and validates them.
+   * @param serviceComponent
+   * @param desiredVersion current version for cluster. All host component versions
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateServiceComponent(ServiceComponent serviceComponent,
+                                        String desiredVersion,
+                                        PrerequisiteCheck prerequisiteCheck,
+                                        List<String> errorMessages) {
+    if (!serviceComponent.isVersionAdvertised())
+      return;
+
+    Map<String, ServiceComponentHost> serviceComponentHosts = serviceComponent.getServiceComponentHosts();
+    for (ServiceComponentHost serviceComponentHost : serviceComponentHosts.values()) {
+      validateServiceComponentHost(serviceComponentHost, desiredVersion, prerequisiteCheck, errorMessages);
+    }
+  }
+
+  /**
+   * Validates host component. If upgrade state of host component is VERSION_MISMATCH,
+   * adds hostname to a Failed On map of prerequisite check, and adds all other
+   * host component version details to errorMessages
+   * @param serviceComponentHost
+   * @param desiredVersion current version for cluster. Component host version
+   *                       should match it
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void validateServiceComponentHost(ServiceComponentHost serviceComponentHost,
+                                            String desiredVersion,
+                                            PrerequisiteCheck prerequisiteCheck,
+                                            List<String> errorMessages) {
+    String actualVersion = serviceComponentHost.getVersion();
+    if (StringUtils.equals(desiredVersion, actualVersion))
+      return;
+
+    String hostName = serviceComponentHost.getHostName();
+    String serviceComponentName = serviceComponentHost.getServiceComponentName();
+
+    String message = hostName + "/" + serviceComponentName
+        + " desired version: " + desiredVersion
+        + ", actual version: " + actualVersion;
+    prerequisiteCheck.getFailedOn().add(hostName);
+    errorMessages.add(message);
+  }
+
+  /**
+   * Validates component version in a cluster that has no CURRENT version defined.
+   * If there is more than one actual host component version in cluster, collects
+   * details for warning.
+   * @param services
+   * @param prerequisiteCheck
+   * @param errorMessages
+   */
+  private void listAllComponentsWithHostVersions(Collection<Service> services,
+                                                 PrerequisiteCheck prerequisiteCheck,
+                                                 List<String> errorMessages) {
+    if (countDistinctVersionsOnHosts(services) <= 1)
+      return;
+
+    for (Service service : services) {
+      for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
+        if (!serviceComponent.isVersionAdvertised())
+          continue;
+
+        ArrayList<Object> hostVersions = new ArrayList<>();
+        for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
+          hostVersions.add(serviceComponentHost.getVersion());
+          prerequisiteCheck.getFailedOn().add(serviceComponentHost.getHostName());
+        }
+        String message = serviceComponent.getName() + " host versions: " + hostVersions;
+        errorMessages.add(message);
+      }
+    }
+  }
+
+  /**
+   * Iterates over services, and enumerates reported versions of host components.
+   * @param services collection of services
+   * @return number of distinct actual versions found
+   */
+  private int countDistinctVersionsOnHosts(Collection<Service> services) {
+    HashSet<Object> versions = new HashSet<>();
+    for (Service service : services) {
+      for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
+        if (!serviceComponent.isVersionAdvertised())
+          continue;
+        for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
+          versions.add(serviceComponentHost.getVersion());
+        }
+      }
+    }
+    return versions.size();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eba6fd3/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java b/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
new file mode 100644
index 0000000..f525b40
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/checks/ServiceComponentHostVersionMatchCheckTest.java
@@ -0,0 +1,128 @@
+/**
+ * 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.checks;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provider;
+import org.apache.ambari.server.controller.PrereqCheckRequest;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.stack.PrereqCheckStatus;
+import org.apache.ambari.server.state.stack.PrerequisiteCheck;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ServiceComponentHostVersionMatchCheckTest {
+  private static final String CLUSTER_NAME = "cluster1";
+  private static final String FIRST_SERVICE_NAME = "service1";
+  private static final String FIRST_SERVICE_COMPONENT_NAME = "component1";
+  private static final String FIRST_SERVICE_COMPONENT_HOST_NAME = "host1";
+  private static final String SECOND_SERVICE_COMPONENT_HOST_NAME = "host2";
+  private static final String CURRENT_VERSION = "1.1.1.1";
+  private static final String OTHER_VERSION = "1.2.3.4";
+  private ServiceComponentHostVersionMatchCheck versionMismatchCheck;
+  private Cluster cluster;
+  private ServiceComponentHost firstServiceComponentHost;
+  private ServiceComponentHost secondServiceComponentHost;
+
+  @Before
+  public void setUp() throws Exception {
+    final Clusters clusters = mock(Clusters.class);
+    versionMismatchCheck = new ServiceComponentHostVersionMatchCheck();
+    versionMismatchCheck.clustersProvider = new Provider<Clusters>() {
+      @Override
+      public Clusters get() {
+        return clusters;
+      }
+    };
+
+    cluster = mock(Cluster.class, RETURNS_DEEP_STUBS);
+    when(clusters.getCluster(CLUSTER_NAME)).thenReturn(cluster);
+
+    Service firstService = mock(Service.class);
+    Map<String, Service> services = ImmutableMap.of(FIRST_SERVICE_NAME, firstService);
+    when(cluster.getServices()).thenReturn(services);
+
+    ServiceComponent firstServiceComponent = mock(ServiceComponent.class);
+    Map<String, ServiceComponent> components = ImmutableMap.of(FIRST_SERVICE_COMPONENT_NAME, firstServiceComponent);
+    when(firstServiceComponent.isVersionAdvertised()).thenReturn(true);
+    when(firstService.getServiceComponents()).thenReturn(components);
+
+    firstServiceComponentHost = mock(ServiceComponentHost.class);
+    secondServiceComponentHost = mock(ServiceComponentHost.class);
+    Map<String, ServiceComponentHost> firstServiceComponentHosts = ImmutableMap.of(
+        FIRST_SERVICE_COMPONENT_HOST_NAME, firstServiceComponentHost,
+        SECOND_SERVICE_COMPONENT_HOST_NAME, secondServiceComponentHost
+    );
+    when(firstServiceComponent.getServiceComponentHosts()).thenReturn(firstServiceComponentHosts);
+  }
+
+  @Test
+  public void testWarningWhenHostWithVersionOtherThanCurrentClusterVersionExists() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion().getVersion()).thenReturn(CURRENT_VERSION);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(OTHER_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.WARNING, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenHostWithVersionOtherThanCurrentClusterVersionDoesNotExist() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion().getVersion()).thenReturn(CURRENT_VERSION);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenHostsWithDifferentVersionsExistAndCurrentClusterVersionIsUnknown() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion()).thenReturn(null);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(OTHER_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.WARNING, check.getStatus());
+  }
+
+  @Test
+  public void testWarningWhenAllHostsHaveSameVersionAndCurrentClusterVersionIsUnknown() throws Exception {
+    when(cluster.getCurrentClusterVersion().getRepositoryVersion()).thenReturn(null);
+    when(firstServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+    when(secondServiceComponentHost.getVersion()).thenReturn(CURRENT_VERSION);
+
+    PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
+    versionMismatchCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
+    Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+  }
+}
\ No newline at end of file