You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/01/22 01:33:52 UTC

[1/2] ambari git commit: AMBARI-9225. RU - Handle components that don't advertise a version when propagation HostComponentState through ClusterVersion, and handle ClusterVersion and HostVersion state transitions correctly (alejandro)

Repository: ambari
Updated Branches:
  refs/heads/trunk fcc6a4ccf -> aab897d57


http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
index bb0836f..8ebf2ce 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
@@ -113,7 +113,7 @@ public class ServiceComponentHostTest {
     Cluster c1 = clusters.getCluster("C1");
     c1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     metaInfo.init();
     clusters.mapHostToCluster("h1","C1");
   }
@@ -721,7 +721,7 @@ public class ServiceComponentHostTest {
     StackId stackId = new StackId(stackVersion);
     c2.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
 
@@ -948,7 +948,7 @@ public class ServiceComponentHostTest {
     StackId stackId = new StackId(stackVersion);
     c2.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
@@ -1083,7 +1083,7 @@ public class ServiceComponentHostTest {
     StackId stackId = new StackId(stackVersion);
     c2.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
 
@@ -1127,7 +1127,7 @@ public class ServiceComponentHostTest {
     StackId stackId = new StackId(stackVersion);
     c2.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
 


[2/2] ambari git commit: AMBARI-9225. RU - Handle components that don't advertise a version when propagation HostComponentState through ClusterVersion, and handle ClusterVersion and HostVersion state transitions correctly (alejandro)

Posted by al...@apache.org.
AMBARI-9225. RU - Handle components that don't advertise a version when propagation HostComponentState through ClusterVersion, and handle ClusterVersion and HostVersion state transitions correctly (alejandro)


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

Branch: refs/heads/trunk
Commit: aab897d57a9c53a66205421a64e3c265bfd231a0
Parents: fcc6a4c
Author: Alejandro Fernandez <af...@hortonworks.com>
Authored: Mon Jan 19 12:28:28 2015 -0800
Committer: Alejandro Fernandez <af...@hortonworks.com>
Committed: Wed Jan 21 16:11:19 2015 -0800

----------------------------------------------------------------------
 .../ambari/server/agent/HeartBeatHandler.java   |   9 +-
 .../StackServiceComponentResponse.java          |  26 +++
 .../ClusterStackVersionResourceProvider.java    |   2 +-
 .../StackServiceComponentResourceProvider.java  |   6 +
 .../DistributeRepositoriesActionListener.java   |  73 ++++----
 .../ambari/server/stack/ComponentModule.java    |   1 +
 .../ambari/server/state/ComponentInfo.java      |  21 +++
 .../server/state/ServiceComponentHost.java      |   4 +-
 .../server/state/cluster/ClusterImpl.java       | 147 ++++++++++++----
 .../svccomphost/ServiceComponentHostImpl.java   | 111 ++++++++----
 .../common-services/AMS/0.1.0/metainfo.xml      |   2 +
 .../common-services/HDFS/2.1.0.2.0/metainfo.xml |   1 +
 .../HIVE/0.12.0.2.0/metainfo.xml                |   1 +
 .../KERBEROS/1.10.3-10/metainfo.xml             |   1 +
 .../server/agent/TestHeartbeatHandler.java      |   2 +-
 .../server/agent/TestHeartbeatMonitor.java      |  10 +-
 .../AmbariManagementControllerTest.java         |  21 ++-
 .../StackDefinedPropertyProviderTest.java       |   2 +-
 .../internal/UpgradeResourceProviderTest.java   |   3 +-
 .../apache/ambari/server/events/EventsTest.java |   2 +-
 .../apache/ambari/server/orm/OrmTestHelper.java |   2 +-
 .../server/state/ServiceComponentTest.java      |   2 +-
 .../ambari/server/state/UpgradeHelperTest.java  |   2 +-
 .../server/state/cluster/ClusterTest.java       | 171 +++++++++++++++----
 .../server/state/cluster/ClustersTest.java      |  15 +-
 .../ambari/server/state/host/HostTest.java      |   4 +-
 .../svccomphost/ServiceComponentHostTest.java   |  10 +-
 27 files changed, 460 insertions(+), 191 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index 2c1f94f..092f9d5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -411,7 +411,6 @@ public class HeartBeatHandler {
     }
     Collection<HostRoleCommand> commands = actionManager.getTasks(taskIds);
 
-    Set<ServiceComponentHost> scHostsRequireRecalculation = new HashSet<ServiceComponentHost>();
     Iterator<HostRoleCommand> hostRoleCommandIterator = commands.iterator();
     for (CommandReport report : reports) {
 
@@ -485,7 +484,8 @@ public class HeartBeatHandler {
                   if (previousVersion != null && !previousVersion.equals("UNKNOWN")) {
                     scHost.setUpgradeState(UpgradeState.COMPLETE);
                   }
-                  scHostsRequireRecalculation.add(scHost);
+                  String repoVersion = scHost.recalculateHostVersionState();
+                  cl.recalculateClusterVersionState(repoVersion);
                 }
               }
             }
@@ -558,10 +558,7 @@ public class HeartBeatHandler {
         }
       }
     }
-    //Recalculate host versions
-    for (ServiceComponentHost serviceComponentHost : scHostsRequireRecalculation) {
-      serviceComponentHost.recalculateHostVersionState();
-    }
+
     //Update state machines from reports
     actionManager.processTaskResponse(hostname, reports, commands);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
index 47d153b..7924ba8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
@@ -76,6 +76,11 @@ public class StackServiceComponentResponse {
   private String cardinality;
 
   /**
+   * does the component need to advertise a version
+   */
+  private boolean advertiseVersion;
+
+  /**
    * auto deploy information
    */
   private AutoDeployInfo autoDeploy;
@@ -99,6 +104,7 @@ public class StackServiceComponentResponse {
     isClient = component.isClient();
     isMaster = component.isMaster();
     cardinality = component.getCardinality();
+    advertiseVersion = component.isAdvertiseVersion();
     autoDeploy = component.getAutoDeploy();
 
     // the custom command names defined for this component
@@ -276,6 +282,26 @@ public class StackServiceComponentResponse {
     this.cardinality = cardinality;
   }
 
+
+  /**
+   * Get whether the components needs to advertise a version.
+   *
+   * @return Whether the components needs to advertise a version
+   */
+  public boolean isAdvertiseVersion() {
+    return advertiseVersion;
+  }
+
+  /**
+   * Set whether the component needs to advertise a version.
+   *
+   * @param advertiseVersion whether the component needs to advertise a version
+   */
+  public void setAdvertiseVersion(boolean advertiseVersion) {
+    this.advertiseVersion = advertiseVersion;
+  }
+
+
   /**
    * Get auto deploy information.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
index 6d503e2..eb0acb6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
@@ -386,8 +386,8 @@ public class ClusterStackVersionResourceProvider extends AbstractControllerResou
         // Move CSV into INSTALLING state (retry installation)
         cluster.transitionClusterVersion(stackId, desiredRepoVersion, RepositoryVersionState.INSTALLING);
       }
+      // Will also initialize all Host Versions in an INSTALLING state.
       cluster.inferHostVersions(existingCSVer);
-      cluster.recalculateClusterVersionState(desiredRepoVersion);
 
       req.persist();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
index 1f23773..1ad0a0a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
@@ -59,6 +59,9 @@ public class StackServiceComponentResourceProvider extends
   private static final String CARDINALITY_ID = PropertyHelper.getPropertyId(
       "StackServiceComponents", "cardinality");
 
+  private static final String ADVERTISE_VERSION_ID = PropertyHelper.getPropertyId(
+      "StackServiceComponents", "advertise_version");
+
   private static final String CUSTOM_COMMANDS_PROPERTY_ID = PropertyHelper.getPropertyId(
       "StackServiceComponents", "custom_commands");
 
@@ -135,6 +138,9 @@ public class StackServiceComponentResourceProvider extends
       setResourceProperty(resource, CARDINALITY_ID,
           response.getCardinality(), requestedIds);
 
+      setResourceProperty(resource, ADVERTISE_VERSION_ID,
+          response.isAdvertiseVersion(), requestedIds);
+
       setResourceProperty(resource, CUSTOM_COMMANDS_PROPERTY_ID,
           response.getCustomCommands(), requestedIds);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/DistributeRepositoriesActionListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/DistributeRepositoriesActionListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/DistributeRepositoriesActionListener.java
index feca260..06e0a7f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/DistributeRepositoriesActionListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/DistributeRepositoriesActionListener.java
@@ -108,19 +108,18 @@ public class DistributeRepositoriesActionListener {
       LOG.debug(event.toString());
     }
 
-    RepositoryVersionState newHostState;
-
-    String repositoryVersion = null;
+    RepositoryVersionState newHostState = RepositoryVersionState.INSTALL_FAILED;
     Long clusterId = event.getClusterId();
+    if (clusterId == null) {
+      LOG.error("Distribute Repositories expected a cluster Id for host " + event.getHostname());
+      return;
+    }
+
 
+    String repositoryVersion = null;
+    
     if (event.getCommandReport() == null) {
-      // Something has gone wrong on host
-      // That's why we mark all host stack versions that are at
-      // INSTALLING state as failed
-      // This decision should not be a problem because there should not be more
-      // then 1 concurrent host stack version installation
-      LOG.warn("Command report is null, marking action as INSTALL_FAILED");
-      newHostState = RepositoryVersionState.INSTALL_FAILED;
+      LOG.error("Command report is null, marking action as INSTALL_FAILED");
     } else {
       // Parse structured output
       try {
@@ -129,48 +128,34 @@ public class DistributeRepositoriesActionListener {
                 DistributeRepositoriesStructuredOutput.class);
         if (event.getCommandReport().getStatus().equals(HostRoleStatus.COMPLETED.toString())) {
           newHostState = RepositoryVersionState.INSTALLED;
-        } else {
-          newHostState = RepositoryVersionState.INSTALL_FAILED;
         }
         repositoryVersion = structuredOutput.getInstalledRepositoryVersion();
       } catch (JsonSyntaxException e) {
         LOG.error("Can not parse structured output %s", e);
-        newHostState = RepositoryVersionState.INSTALL_FAILED;
       }
     }
-    List<HostVersionEntity> hostVersions = hostVersionDAO.get().findByHost(event.getHostname());
-    for (HostVersionEntity hostVersion : hostVersions) {
-      if (repositoryVersion != null && ! hostVersion.getRepositoryVersion().getVersion().equals(repositoryVersion)) {
-        // Are we going to update state of a concrete host stack version?
-        continue;
+
+    if (repositoryVersion != null) {
+      List<HostVersionEntity> hostVersions = hostVersionDAO.get().findByHost(event.getHostname());
+      HostVersionEntity foundHostVersion = null;
+
+      for (HostVersionEntity hostVersion : hostVersions) {
+        if (hostVersion.getRepositoryVersion().getVersion().equals(repositoryVersion)) {
+          foundHostVersion = hostVersion;
+          break;
+        }
       }
-      // Typically, there will be single execution of code below
-      if (hostVersion.getState() == RepositoryVersionState.INSTALLING) {
-        hostVersion.setState(newHostState);
-
-        if (clusterId != null) { // Update state of a cluster stack version
-          try {
-            Cluster cluster = clusters.get().getClusterById(clusterId);
-            cluster.recalculateClusterVersionState(hostVersion.getRepositoryVersion().getVersion());
-          } catch (AmbariException e) {
-            LOG.error("Can not get cluster with Id " + clusterId, e);
-          }
-        } else {
-          LOG.warn("Can not determine cluster for stack version state update");
-          // Recalculate state of all clusters to ensure consistency
-          try {
-            Set<Cluster> clustersForHost = clusters.get().getClustersForHost(event.getHostname());
-            for (Cluster cluster : clustersForHost) {
-              cluster.recalculateClusterVersionState(hostVersion.getRepositoryVersion().getVersion());
-            }
-          } catch (AmbariException e) {
-            LOG.error("Can not update state of clusters", e);
-          }
+
+      if (foundHostVersion != null && foundHostVersion.getState() == RepositoryVersionState.INSTALLING) {
+        foundHostVersion.setState(newHostState);
+
+        // Update state of a cluster stack version
+        try {
+          Cluster cluster = clusters.get().getClusterById(clusterId);
+          cluster.recalculateClusterVersionState(foundHostVersion.getRepositoryVersion().getVersion());
+        } catch (AmbariException e) {
+          LOG.error("Cannot get cluster with Id " + clusterId.toString(), e);
         }
-      } else {
-        LOG.error(
-                "Can not transition host stack version state from {} to {} for " +
-                                "host {}", hostVersion.getState(), newHostState, event.getHostname());
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java
index f4e9999..f79db0c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java
@@ -71,6 +71,7 @@ public class ComponentModule extends BaseModule<ComponentModule, ComponentInfo>
     if (componentInfo.getCardinality() == null) {
       componentInfo.setCardinality(parentInfo.getCardinality());
     }
+    componentInfo.setAdvertiseVersion(parentInfo.isAdvertiseVersion());
     if (componentInfo.getAutoDeploy() == null) {
       componentInfo.setAutoDeploy(parentInfo.getAutoDeploy());
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
index dcfd00f..cbf16e3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
@@ -35,6 +35,16 @@ public class ComponentInfo {
   private String cardinality;
 
   /**
+   * By default, all Components should advertise a version through a mechanism like hdp-select.
+   * The version must be present the structured output.in the {"version": "#.#.#.#-###"}
+   * For example, Masters will typically advertise the version upon a RESTART.
+   * Whereas clients will advertise the version when INSTALLED.
+   * Some components do not need to advertise a version because it is either redundant, or they don't have a mechanism
+   * at the moment. For instance, ZKFC has the same version as NameNode, while AMS and KERBEROS do not have a mechanism.
+   */
+  private boolean advertiseVersion = true;
+
+  /**
   * Added at schema ver 2
   */
   private CommandScriptDefinition commandScript;
@@ -89,6 +99,7 @@ public class ComponentInfo {
     category = prototype.category;
     deleted = prototype.deleted;
     cardinality = prototype.cardinality;
+    advertiseVersion = prototype.advertiseVersion;
     clientsToUpdateConfigs = prototype.clientsToUpdateConfigs;
     commandScript = prototype.commandScript;
     customCommands = prototype.customCommands;
@@ -223,6 +234,14 @@ public class ComponentInfo {
     return cardinality;
   }
 
+  public void setAdvertiseVersion(boolean advertiseVersion) {
+    this.advertiseVersion = advertiseVersion;
+  }
+
+  public boolean isAdvertiseVersion() {
+    return advertiseVersion;
+  }
+
   public List<String> getClientsToUpdateConfigs() {
     return clientsToUpdateConfigs;
   }
@@ -241,6 +260,7 @@ public class ComponentInfo {
     if (deleted != that.deleted) return false;
     if (autoDeploy != null ? !autoDeploy.equals(that.autoDeploy) : that.autoDeploy != null) return false;
     if (cardinality != null ? !cardinality.equals(that.cardinality) : that.cardinality != null) return false;
+    if (advertiseVersion != that.advertiseVersion) return false;
     if (category != null ? !category.equals(that.category) : that.category != null) return false;
     if (clientConfigFiles != null ? !clientConfigFiles.equals(that.clientConfigFiles) : that.clientConfigFiles != null)
       return false;
@@ -265,6 +285,7 @@ public class ComponentInfo {
     result = 31 * result + (category != null ? category.hashCode() : 0);
     result = 31 * result + (deleted ? 1 : 0);
     result = 31 * result + (cardinality != null ? cardinality.hashCode() : 0);
+    result = 31 * result + (advertiseVersion ? 1 : 0);
     result = 31 * result + (commandScript != null ? commandScript.hashCode() : 0);
     result = 31 * result + (clientConfigFiles != null ? clientConfigFiles.hashCode() : 0);
     result = 31 * result + (customCommands != null ? customCommands.hashCode() : 0);

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
index e337153..0c614dd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
@@ -203,9 +203,9 @@ public interface ServiceComponentHost {
 
   /**
    * Changes host version state according to state of the components installed on the host.
-   *
+   * @return The version number associated with that component in the host
    * @throws AmbariException if host is detached from the cluster
    */
-  public void recalculateHostVersionState() throws AmbariException;
+  public String recalculateHostVersionState() throws AmbariException;
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index bf5bf50..220a5af 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -631,10 +631,10 @@ public class ClusterImpl implements Cluster {
           throw new ConfigGroupNotFoundException(getClusterName(), id.toString());
         }
         LOG.debug("Deleting Config group"
-          + ", clusterName = " + getClusterName()
-          + ", groupName = " + configGroup.getName()
-          + ", groupId = " + configGroup.getId()
-          + ", tag = " + configGroup.getTag());
+            + ", clusterName = " + getClusterName()
+            + ", groupName = " + configGroup.getName()
+            + ", groupId = " + configGroup.getId()
+            + ", tag = " + configGroup.getTag());
 
         configGroup.delete();
         clusterConfigGroups.remove(id);
@@ -848,9 +848,9 @@ public class ClusterImpl implements Cluster {
 
         if (schToRemove == null) {
           LOG.warn("Unavailable in per host cache. ServiceComponentHost"
-            + ", serviceName=" + serviceName
-            + ", serviceComponentName" + componentName
-            + ", hostname= " + hostname);
+              + ", serviceName=" + serviceName
+              + ", serviceComponentName" + componentName
+              + ", hostname= " + hostname);
         }
 
         if (LOG.isDebugEnabled()) {
@@ -1131,6 +1131,13 @@ public class ClusterImpl implements Cluster {
     return clusterVersionDAO.findByCluster(this.getClusterName());
   }
 
+  /**
+   * During the Finalize Action, want to transition all Host Versions from UPGRADED to CURRENT, and the last CURRENT one to INSTALLED.
+   * @param hostNames Collection of host names
+   * @param currentClusterVersion Entity that contains the cluster's current stack (with its name and version)
+   * @param desiredState Desired state must be {@link RepositoryVersionState#CURRENT} or {@link RepositoryVersionState#UPGRADING}
+   * @throws AmbariException
+   */
   @Override
   public void mapHostVersions(Set<String> hostNames, ClusterVersionEntity currentClusterVersion, RepositoryVersionState desiredState) throws AmbariException {
     if (currentClusterVersion == null) {
@@ -1139,7 +1146,6 @@ public class ClusterImpl implements Cluster {
 
     final Set<RepositoryVersionState> validStates = new HashSet<RepositoryVersionState>(){{
       add(RepositoryVersionState.CURRENT);
-      add(RepositoryVersionState.UPGRADING);
     }};
 
     if (!validStates.contains(desiredState)) {
@@ -1195,6 +1201,13 @@ public class ClusterImpl implements Cluster {
     }
   }
 
+  /**
+   * Because this is a top-down approach, it should only be called for the purposes of bootstrapping data, such as
+   * installing a brand new cluster or initiating an Upgrade.
+   * @param sourceClusterVersion cluster version to be queried for a stack name/version info and desired RepositoryVersionState.
+   * The only valid state of this cluster version is {@link RepositoryVersionState#INSTALLING}
+   * @throws AmbariException
+   */
   @Override
   public void inferHostVersions(ClusterVersionEntity sourceClusterVersion) throws AmbariException {
     if (sourceClusterVersion == null) {
@@ -1252,17 +1265,71 @@ public class ClusterImpl implements Cluster {
     }
   }
 
-  private RepositoryVersionState getWorstState(RepositoryVersionState currentState, RepositoryVersionState newState) {
-    return currentState.getPriority() < newState.getPriority() ? currentState : newState;
+  /**
+   * Calculate the effective Cluster Version State based on the state of its hosts.
+   *
+   * CURRENT: all hosts are CURRENT
+   * UPGRADE_FAILED: at least one host in UPGRADE_FAILED
+   * UPGRADED: all hosts are UPGRADED
+   * UPGRADING: at least one host is UPGRADING, and the rest in UPGRADING|INSTALLED
+   * INSTALLED: all hosts in INSTALLED
+   * INSTALL_FAILED: at least one host in INSTALL_FAILED
+   * INSTALLING: all hosts in INSTALLING. Notice that if one host is CURRENT and another is INSTALLING, then the
+   * effective version will be OUT_OF_SYNC.
+   * OUT_OF_SYNC: otherwise
+   * @param stateToHosts Map from state to the collection of hosts with that state
+   * @return Return the effective Cluster Version State
+   */
+  private RepositoryVersionState getEffectiveState(Map<RepositoryVersionState, Set<String>> stateToHosts) {
+    if (stateToHosts == null || stateToHosts.keySet().size() < 1) {
+      return null;
+    }
+
+    int totalHosts = 0;
+    for (Set<String> hosts : stateToHosts.values()) {
+      totalHosts += hosts.size();
+    }
+
+    if (stateToHosts.containsKey(RepositoryVersionState.CURRENT) && stateToHosts.get(RepositoryVersionState.CURRENT).size() == totalHosts) {
+      return RepositoryVersionState.CURRENT;
+    }
+    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADE_FAILED) && !stateToHosts.get(RepositoryVersionState.UPGRADE_FAILED).isEmpty()) {
+      return RepositoryVersionState.UPGRADE_FAILED;
+    }
+    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADED) && stateToHosts.get(RepositoryVersionState.UPGRADED).size() == totalHosts) {
+      return RepositoryVersionState.UPGRADED;
+    }
+    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADING) && !stateToHosts.get(RepositoryVersionState.UPGRADING).isEmpty()) {
+      return RepositoryVersionState.UPGRADING;
+    }
+    if (stateToHosts.containsKey(RepositoryVersionState.INSTALLED) && stateToHosts.get(RepositoryVersionState.INSTALLED).size() == totalHosts) {
+      return RepositoryVersionState.INSTALLED;
+    }
+    if (stateToHosts.containsKey(RepositoryVersionState.INSTALL_FAILED) && !stateToHosts.get(RepositoryVersionState.INSTALL_FAILED).isEmpty()) {
+      return RepositoryVersionState.INSTALL_FAILED;
+    }
+
+    final int totalINSTALLING = stateToHosts.containsKey(RepositoryVersionState.INSTALLING) ? stateToHosts.get(RepositoryVersionState.INSTALLING).size() : 0;
+    final int totalINSTALLED = stateToHosts.containsKey(RepositoryVersionState.INSTALLED) ? stateToHosts.get(RepositoryVersionState.INSTALLED).size() : 0;
+    if (totalINSTALLING + totalINSTALLED == totalHosts) {
+      return RepositoryVersionState.INSTALLING;
+    }
+
+    // Also returns when have a mix of CURRENT and INSTALLING|INSTALLED|UPGRADING|UPGRADED
+    return RepositoryVersionState.OUT_OF_SYNC;
   }
 
   @Override
   public void recalculateClusterVersionState(String repositoryVersion) throws AmbariException {
+    if (repositoryVersion == null) {
+      return;
+    }
+
     clusterGlobalLock.readLock().lock();
     try {
       readWriteLock.writeLock().lock();
       try {
-        Map<String, Host> hosts = clusters.getHostsForCluster(this.getClusterName());
+        // Part 1, bootstrap cluster version if necessary.
         StackId stackId = getCurrentStackVersion();
 
         ClusterVersionEntity clusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(this.getClusterName(),
@@ -1270,6 +1337,7 @@ public class ClusterImpl implements Cluster {
 
         if (clusterVersion == null) {
           if (clusterVersionDAO.findByCluster(getClusterName()).isEmpty()) {
+            // During an Ambari Upgrade from 1.7.0 -> 2.0.0, the Cluster Version will not exist, so bootstrap it.
             createClusterVersionInternal(stackId.getStackId(), repositoryVersion, AuthorizationHelper.getAuthenticatedName(configuration.getAnonymousAuditName()), RepositoryVersionState.UPGRADING);
             clusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(this.getClusterName(), stackId.getStackId(), repositoryVersion);
           } else {
@@ -1277,7 +1345,8 @@ public class ClusterImpl implements Cluster {
           }
         }
 
-        RepositoryVersionState worstState;
+        // Ignore if cluster version is CURRENT or UPGRADE_FAILED
+        RepositoryVersionState lowestPriorityState;
         if (clusterVersion.getState() != RepositoryVersionState.INSTALL_FAILED &&
                 clusterVersion.getState() != RepositoryVersionState.OUT_OF_SYNC &&
                 clusterVersion.getState() != RepositoryVersionState.INSTALLING &&
@@ -1287,32 +1356,46 @@ public class ClusterImpl implements Cluster {
           // anything else is not supported as of now
           return;
         }
-        worstState = RepositoryVersionState.CURRENT;
+
+        // Part 2, check for transitions.
+        Map<String, Host> hosts = clusters.getHostsForCluster(this.getClusterName());
+
+        Map<RepositoryVersionState, Set<String>> stateToHosts = new HashMap<RepositoryVersionState, Set<String>>();
         for (Host host : hosts.values()) {
           String hostName = host.getHostName();
+          HostVersionEntity hostVersion = hostVersionDAO.findByClusterStackVersionAndHost(this.getClusterName(), stackId.getStackId(), repositoryVersion, hostName);
+          if (hostVersion == null) {
+            // It is likely that the services are still being installed on the cluster, so not all agents
+            // have had a chance to heartbeat and insert their own Host Version as part of recalculateHostVersionState().
+            // Therefore, we should not allow any transitions yet.
+            LOG.debug("Skipping transitioning the cluster version because host " + hostName + " does not have a version yet.");
+            return;
+          }
+
+          RepositoryVersionState hostState = hostVersion.getState();
           if (host.getState() != HostState.HEALTHY) {
-            worstState = getWorstState(worstState, RepositoryVersionState.OUT_OF_SYNC);
-            LOG.warn(String.format("Host %s is in unhealthy state, treating as %s",
-                    hostName, worstState));
+            hostState = RepositoryVersionState.OUT_OF_SYNC;
+            LOG.warn(String.format("Host %s is in unhealthy state, treating as %s", hostName, hostState));
           }
 
-          HostVersionEntity hostVersion = hostVersionDAO.findByClusterStackVersionAndHost(this.getClusterName(), stackId.getStackId(), repositoryVersion, hostName);
-          if (clusterVersionDAO.findByClusterAndStateCurrent(getClusterName()) != null) { //TODO workaround to skip this check during clean install
-            if (hostVersion == null) {
-              LOG.warn(String.format("Repo version %s is not installed on host %s",
-                      repositoryVersion, hostName));
-              worstState = getWorstState(worstState, RepositoryVersionState.OUT_OF_SYNC);
-            } else {
-              worstState = getWorstState(worstState, hostVersion.getState());
-            }
+          if (stateToHosts.containsKey(hostState)) {
+            stateToHosts.get(hostState).add(hostName);
+          } else {
+            Set<String> hostsInState = new HashSet<String>();
+            hostsInState.add(hostName);
+            stateToHosts.put(hostState, hostsInState);
           }
         }
-        if (worstState != clusterVersion.getState()) {
-          // Any mismatch will be catched while transitioning
-          transitionClusterVersion(stackId.getStackId(), repositoryVersion, worstState);
-        }
-        clusterVersionDAO.merge(clusterVersion);
 
+        RepositoryVersionState effectiveClusterVersionState = getEffectiveState(stateToHosts);
+        if (effectiveClusterVersionState != null && effectiveClusterVersionState != clusterVersion.getState()) {
+          // Any mismatch will be caught while transitioning, and raise an exception.
+          try {
+            transitionClusterVersion(stackId.getStackId(), repositoryVersion, effectiveClusterVersionState);
+          } catch (AmbariException e) {
+            ;
+          }
+        }
       } finally {
         readWriteLock.writeLock().unlock();
       }
@@ -1367,13 +1450,12 @@ public class ClusterImpl implements Cluster {
     Set<RepositoryVersionState> allowedStates = new HashSet<RepositoryVersionState>();
     Collection<ClusterVersionEntity> allClusterVersions = getAllClusterVersions();
     if (allClusterVersions == null || allClusterVersions.isEmpty()) {
-      allowedStates.add(RepositoryVersionState.CURRENT);
       allowedStates.add(RepositoryVersionState.UPGRADING);
     } else {
       allowedStates.add(RepositoryVersionState.INSTALLING);
     }
 
-    if (! allowedStates.contains(state)) {
+    if (!allowedStates.contains(state)) {
       throw new AmbariException("The allowed state for a new cluster version must be within " + allowedStates);
     }
 
@@ -1430,7 +1512,6 @@ public class ClusterImpl implements Cluster {
             case INSTALLED:
               allowedStates.add(RepositoryVersionState.INSTALLING);
               allowedStates.add(RepositoryVersionState.UPGRADING);
-              allowedStates.add(RepositoryVersionState.UPGRADED);
               allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
               break;
             case OUT_OF_SYNC:

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
index 3593eb3..372325d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
@@ -40,12 +40,14 @@ import org.apache.ambari.server.events.MaintenanceModeEvent;
 import org.apache.ambari.server.events.ServiceComponentInstalledEvent;
 import org.apache.ambari.server.events.ServiceComponentUninstalledEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.HostComponentStateDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntityPK;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
@@ -57,6 +59,7 @@ import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntityPK;
 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.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostComponentAdminState;
@@ -65,7 +68,6 @@ import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.SecurityState;
-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.ServiceComponentHostEvent;
@@ -112,6 +114,8 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
   @Inject
   Gson gson;
   @Inject
+  ClusterVersionDAO clusterVersionDAO;
+  @Inject
   HostComponentStateDAO hostComponentStateDAO;
   @Inject
   HostComponentDesiredStateDAO hostComponentDesiredStateDAO;
@@ -1713,13 +1717,15 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
     }
   }
 
+  @Transactional
   @Override
-  public void recalculateHostVersionState() throws AmbariException {
+  public String recalculateHostVersionState() throws AmbariException {
     final String version = getVersion();
     if (version.equals("UNKNOWN")) {
       // recalculate only if some particular version is set
-      return;
+      return null;
     }
+
     final String hostName = getHostName();
     final HostEntity host = hostDAO.findByName(hostName);
     final Set<Cluster> clustersForHost = clusters.getClustersForHost(hostName);
@@ -1729,62 +1735,89 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
     final Cluster cluster = clustersForHost.iterator().next();
     final StackId stack = cluster.getDesiredStackVersion();
     final StackInfo stackInfo = ambariMetaInfo.getStack(stack.getStackName(), stack.getStackVersion());
+
     RepositoryVersionEntity repositoryVersion = repositoryVersionDAO.findByStackAndVersion(stack.getStackId(), version);
+
+    // During an Ambari Upgrade from 1.7.0 -> 2.0.0, the Repo Version will not exist, so bootstrap it.
     if (repositoryVersion == null) {
       LOG.info("Creating new repository version " + stack.getStackName() + "-" + version);
       repositoryVersion = repositoryVersionDAO.create(stack.getStackId(), version, stack.getStackName() + "-" + version,
           repositoryVersionHelper.getUpgradePackageNameSafe(stack.getStackName(), stack.getStackVersion(), version),
           repositoryVersionHelper.serializeOperatingSystems(stackInfo.getRepositories()));
     }
-    HostVersionEntity hostVersionEntity = hostVersionDAO.findByClusterStackVersionAndHost(cluster.getClusterName(), repositoryVersion.getStack(), repositoryVersion.getVersion(), hostName);
-    if (hostVersionEntity == null) {
-      // there is no host version but we have a component on the host of that version. It implies that we have some repo version installed on that host
-      // and we can treat the host as being upgrading to that version
+
+    HostVersionEntity hostVersionEntity = null;
+    List<HostVersionEntity> hostVersions = hostVersionDAO.findByHost(hostName);
+    if (hostVersions == null || hostVersions.isEmpty()) {
+      // Since the host has no versions, allow bootstrapping a version for it.
       hostVersionEntity = new HostVersionEntity(hostName, repositoryVersion, RepositoryVersionState.UPGRADING);
       hostVersionEntity.setHostEntity(host);
       hostVersionDAO.create(hostVersionEntity);
+    } else {
+      hostVersionEntity = hostVersionDAO.findByClusterStackVersionAndHost(cluster.getClusterName(), repositoryVersion.getStack(), repositoryVersion.getVersion(), hostName);
+      if (hostVersionEntity == null) {
+        throw new AmbariException("Host " + hostName + " is expected to have a Host Version for stack " + stack.getStackId());
+      }
     }
 
     final Collection<HostComponentStateEntity> allHostComponents = host.getHostComponentStateEntities();
-    final Collection<HostComponentStateEntity> upgradedHostComponents = new HashSet<HostComponentStateEntity>();
     final Collection<HostComponentStateEntity> versionedHostComponents = new HashSet<HostComponentStateEntity>();
+
     for (HostComponentStateEntity hostComponentStateEntity: allHostComponents) {
-      if (!hostComponentStateEntity.getVersion().equals("UNKNOWN")) {
+      if (!hostComponentStateEntity.getVersion().equalsIgnoreCase("UNKNOWN")) {
         versionedHostComponents.add(hostComponentStateEntity);
-        if (hostComponentStateEntity.getUpgradeState().equals(UpgradeState.COMPLETE) ) {
-          upgradedHostComponents.add(hostComponentStateEntity);
-        }
       }
     }
 
-    // ZKFC is special because it is does not receive a RESTART action during a Rolling Upgrade.
-    @SuppressWarnings("unchecked")
-    final Collection<HostComponentStateEntity> nonUpgradedHostComponents = CollectionUtils.subtract(allHostComponents, upgradedHostComponents);
-    for (HostComponentStateEntity hostComponentStateEntity: nonUpgradedHostComponents) {
-      if (hostComponentStateEntity.getComponentName().equalsIgnoreCase("ZKFC")) {
-        upgradedHostComponents.add(hostComponentStateEntity);
+    final Collection<HostComponentStateEntity> noVersionNeededComponents = new HashSet<HostComponentStateEntity>();
+    final Collection<HostComponentStateEntity> nonVersionedHostComponents = CollectionUtils.subtract(allHostComponents, versionedHostComponents);
+    for (HostComponentStateEntity hostComponentStateEntity: nonVersionedHostComponents) {
+      // Some Components cannot advertise a version. E.g., ZKF, AMS, Kerberos
+      ComponentInfo compInfo = ambariMetaInfo.getComponent(
+          stack.getStackName(), stack.getStackVersion(), hostComponentStateEntity.getServiceName(),
+          hostComponentStateEntity.getComponentName());
+
+      if (!compInfo.isAdvertiseVersion()) {
+        noVersionNeededComponents.add(hostComponentStateEntity);
       }
     }
 
-    if (allHostComponents.size() == upgradedHostComponents.size() && // all components are upgraded
-        haveSameVersion(upgradedHostComponents) && //have the same version
-        (hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED) || hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADING))) {
-      hostVersionEntity.setState(RepositoryVersionState.UPGRADED);
-      hostVersionDAO.merge(hostVersionEntity);
-    } else if (allHostComponents.size() == versionedHostComponents.size() && haveSameVersion(versionedHostComponents) && //all components have same version
-        hostVersionDAO.findByHostAndStateCurrent(cluster.getClusterName(), hostName) == null) { //and no CURRENT version exists
-      hostVersionEntity.setState(RepositoryVersionState.CURRENT);
-      hostVersionDAO.merge(hostVersionEntity);
-    } else if (!upgradedHostComponents.isEmpty() && upgradedHostComponents.size() < allHostComponents.size()) {
-      hostVersionEntity.setState(RepositoryVersionState.UPGRADING);
-      hostVersionDAO.merge(hostVersionEntity);
+    // If 0 or 1 cluster version exists, then a brand new cluster permits the host to transition from UPGRADING->CURRENT
+    // If multiple cluster versions exist, then it means that the change in versions is happening due to an Upgrade,
+    // so should only allow transitioning to UPGRADED or UPGRADING, dependending on further circumstances.
+    List<ClusterVersionEntity> clusterVersions = clusterVersionDAO.findByCluster(cluster.getClusterName());
+    final int versionedPlusNoVersionNeededSize = versionedHostComponents.size() + noVersionNeededComponents.size();
+    if (clusterVersions.size() <= 1) {
+      // Transition from UPGRADING -> CURRENT. This is allowed because Host Version Entity is bootstrapped in an UPGRADING state.
+      if (allHostComponents.size() == versionedPlusNoVersionNeededSize &&
+          (hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADING) || hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADED)) &&
+          haveSameVersion(versionedHostComponents)) {
+        hostVersionEntity.setState(RepositoryVersionState.CURRENT);
+        hostVersionDAO.merge(hostVersionEntity);
+      }
+    } else {
+      // Transition from UPGRADING -> UPGRADED.
+      // We should never transition directly from INSTALLED -> UPGRADED without first going to UPGRADING because
+      // they belong in different phases (1. distribute bits 2. perform upgrade).
+      if (allHostComponents.size() == versionedPlusNoVersionNeededSize &&
+          hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADING) &&
+          haveSameVersion(versionedHostComponents)) {
+        hostVersionEntity.setState(RepositoryVersionState.UPGRADED);
+        hostVersionDAO.merge(hostVersionEntity);
+      } else{
+        // HostVersion is INSTALLED and an upgrade is in-progress because at least 2 components have different versions
+        if (hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED) && versionedHostComponents.size() > 0 &&
+            !haveSameVersion(versionedHostComponents)) {
+          hostVersionEntity.setState(RepositoryVersionState.UPGRADING);
+          hostVersionDAO.merge(hostVersionEntity);
+        }
+      }
     }
-
-    cluster.recalculateClusterVersionState(version);
+    return version;
   }
 
   /**
-   * Checks that every component has the same version.
+   * Checks that every component has the same version
    *
    * @param hostComponents host components
    * @return true if components have the same version
@@ -1795,10 +1828,16 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
       // but just in case: no components passed -> do not change host version
       return false;
     }
-    final String version = hostComponents.iterator().next().getVersion();
+    String firstVersion = null;
     for (HostComponentStateEntity hostComponent : hostComponents) {
-      if (!StringUtils.equals(version, hostComponent.getVersion())) {
-        return false;
+      if (!hostComponent.getVersion().isEmpty()) {
+        if (firstVersion == null) {
+          firstVersion = hostComponent.getVersion();
+        } else {
+          if (!StringUtils.equals(firstVersion, hostComponent.getVersion())) {
+            return false;
+          }
+        }
       }
     }
     return true;

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/resources/common-services/AMS/0.1.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/AMS/0.1.0/metainfo.xml b/ambari-server/src/main/resources/common-services/AMS/0.1.0/metainfo.xml
index bcccb1d..e62211c 100644
--- a/ambari-server/src/main/resources/common-services/AMS/0.1.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/AMS/0.1.0/metainfo.xml
@@ -32,6 +32,7 @@
           <displayName>Metric Collector</displayName>
           <category>MASTER</category>
           <cardinality>1</cardinality>
+          <advertiseVersion>false</advertiseVersion>
           <dependencies>
             <dependency>
               <name>ZOOKEEPER/ZOOKEEPER_SERVER</name>
@@ -52,6 +53,7 @@
           <displayName>Metric Monitor</displayName>
           <category>SLAVE</category>
           <cardinality>ALL</cardinality>
+          <advertiseVersion>false</advertiseVersion>
           <auto-deploy>
             <enabled>true</enabled>
           </auto-deploy>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
index 3936043..0f43ed5 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
@@ -141,6 +141,7 @@
           <category>SLAVE</category>
           <!-- TODO: cardinality is conditional on HA topology -->
           <cardinality>0+</cardinality>
+          <advertiseVersion>false</advertiseVersion>
           <commandScript>
             <script>scripts/zkfc_slave.py</script>
             <scriptType>PYTHON</scriptType>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/metainfo.xml b/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/metainfo.xml
index da662ce..cbf55e0 100644
--- a/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/metainfo.xml
@@ -150,6 +150,7 @@
           <displayName>MySQL Server</displayName>
           <category>MASTER</category>
           <cardinality>0-1</cardinality>
+          <advertiseVersion>false</advertiseVersion>
           <clientsToUpdateConfigs></clientsToUpdateConfigs>
           <commandScript>
             <script>scripts/mysql_server.py</script>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/metainfo.xml b/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/metainfo.xml
index c78ce0b..9a05b6f 100644
--- a/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/metainfo.xml
@@ -34,6 +34,7 @@
           <displayName>Kerberos Client</displayName>
           <category>CLIENT</category>
           <cardinality>ALL</cardinality>
+          <advertiseVersion>false</advertiseVersion>
           <auto-deploy>
             <enabled>true</enabled>
           </auto-deploy>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
index 7e53d76..9bde5b0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
@@ -2200,7 +2200,7 @@ public class TestHeartbeatHandler {
     cluster.setDesiredStackVersion(stackId);
     cluster.setCurrentStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     return cluster;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
index cf183ec..718310c 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
@@ -119,7 +119,7 @@ public class TestHeartbeatMonitor {
     StackId stackId = new StackId("HDP-0.1");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
       add(hostname2);
@@ -205,7 +205,7 @@ public class TestHeartbeatMonitor {
     StackId stackId = new StackId("HDP-0.1");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     Set<String> hostNames = new HashSet<String>() {{
       add(hostname1);
       add(hostname2);
@@ -311,7 +311,7 @@ public class TestHeartbeatMonitor {
     StackId stackId = new StackId("HDP-0.1");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
@@ -422,7 +422,7 @@ public class TestHeartbeatMonitor {
     StackId stackId = new StackId("HDP-0.1");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
@@ -541,7 +541,7 @@ public class TestHeartbeatMonitor {
     StackId stackId = new StackId("HDP-2.0.7");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
index 5350662..805b498 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
@@ -916,7 +916,7 @@ public class AmbariManagementControllerTest {
     StackId stackId = new StackId("HDP-0.1");
     c1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     Service s1 = serviceFactory.createNew(c1, "HDFS");
     Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
     c1.addService(s1);
@@ -999,7 +999,7 @@ public class AmbariManagementControllerTest {
     StackId stackId = new StackId("HDP-0.2");
     c1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     Service s1 = serviceFactory.createNew(c1, "HDFS");
     Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
@@ -1270,17 +1270,20 @@ public class AmbariManagementControllerTest {
 
     foo.setDesiredStackVersion(stackId);
     foo.setCurrentStackVersion(stackId);
-    foo.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    foo.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    foo.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     stackId = new StackId("HDP-0.2");
     c1.setDesiredStackVersion(stackId);
     c1.setCurrentStackVersion(stackId);
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    c1.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     stackId = new StackId("HDP-0.2");
     c2.setDesiredStackVersion(stackId);
     c2.setCurrentStackVersion(stackId);
-    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    c2.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     try {
       set1.clear();
@@ -1489,7 +1492,7 @@ public class AmbariManagementControllerTest {
     c.setDesiredStackVersion(stackId);
     c.setCurrentStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     HostResourceProviderTest.createHosts(controller, requests);
 
@@ -1512,7 +1515,7 @@ public class AmbariManagementControllerTest {
     c.setDesiredStackVersion(stackID);
     c.setCurrentStackVersion(stackID);
     helper.getOrCreateRepositoryVersion(stackID.getStackName(), stackID.getStackVersion());
-    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
     setOsFamily(clusters.getHost("h2"), "redhat", "5.9");
@@ -1858,7 +1861,7 @@ public class AmbariManagementControllerTest {
     StackId stackId = new StackId("HDP-0.1");
     c1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     ClusterRequest r = new ClusterRequest(null, null, null, null);
     Set<ClusterResponse> resp = controller.getClusters(Collections.singleton(r));
@@ -7785,7 +7788,7 @@ public class AmbariManagementControllerTest {
     StackId stackID = new StackId("HDP-0.1");
     c.setDesiredStackVersion(stackID);
     helper.getOrCreateRepositoryVersion(stackID.getStackName(), stackID.getStackVersion());
-    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     clusters.addHost(hostName1);
     setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
     clusters.getHost(hostName1).persist();

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackDefinedPropertyProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackDefinedPropertyProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackDefinedPropertyProviderTest.java
index be40a68..b8c761a 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackDefinedPropertyProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackDefinedPropertyProviderTest.java
@@ -87,7 +87,7 @@ public class StackDefinedPropertyProviderTest {
     StackId stackId = new StackId("HDP-2.0.5");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     clusters.addHost("h1");
     Host host = clusters.getHost("h1");

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
index abd2c64..91a496e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
@@ -141,7 +141,8 @@ public class UpgradeResourceProviderTest {
     StackId stackId = new StackId("HDP-2.1.1");
     cluster.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     clusters.addHost("h1");
     Host host = clusters.getHost("h1");

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/events/EventsTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/events/EventsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/events/EventsTest.java
index eabdc09..de58971 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/events/EventsTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/events/EventsTest.java
@@ -114,7 +114,7 @@ public class EventsTest {
     StackId stackId = new StackId("HDP", "2.0.6");
     m_cluster.setDesiredStackVersion(stackId);
     m_helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    m_cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    m_cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     m_clusters.mapHostToCluster(HOSTNAME, m_clusterName);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
index 9e1ada4..b9fc424 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
@@ -342,7 +342,7 @@ public class OrmTestHelper {
     cluster.setDesiredStackVersion(stackId);
     getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
     cluster.createClusterVersion(stackId.getStackName(),
-        stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+        stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     return cluster;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
index 7ddf50f..c27e600 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
@@ -85,7 +85,7 @@ public class ServiceComponentTest {
     cluster.setDesiredStackVersion(stackId);
     Assert.assertNotNull(cluster);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
 
     Service s = serviceFactory.createNew(cluster, serviceName);
     cluster.addService(s);

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
index e3d29d8..3f7238b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
@@ -255,7 +255,7 @@ public class UpgradeHelperTest {
     helper.getOrCreateRepositoryVersion(c.getDesiredStackVersion().getStackName(),
         c.getDesiredStackVersion().getStackVersion());
     c.createClusterVersion(c.getDesiredStackVersion().getStackName(),
-        c.getDesiredStackVersion().getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+        c.getDesiredStackVersion().getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     for (int i = 0; i < 3; i++) {
       String hostName = "h" + (i+1);
       clusters.addHost(hostName);

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index 04b4f81..c4dfdb8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -160,22 +160,33 @@ public class ClusterTest {
     c1 = clusters.getCluster("c1");
     Assert.assertEquals("c1", c1.getClusterName());
     Assert.assertEquals(1, c1.getClusterId());
+
     clusters.addHost("h1");
-    Host host = clusters.getHost("h1");
-    host.setIPv4("ipv4");
-    host.setIPv6("ipv6");
+    clusters.addHost("h2");
+    Host host1 = clusters.getHost("h1");
+    host1.setIPv4("ipv4");
+    host1.setIPv6("ipv6");
+
+    Host host2 = clusters.getHost("h2");
+    host2.setIPv4("ipv4");
+    host2.setIPv6("ipv6");
 
     Map<String, String> hostAttributes = new HashMap<String, String>();
     hostAttributes.put("os_family", "redhat");
     hostAttributes.put("os_release_version", "5.9");
-    host.setHostAttributes(hostAttributes);
+    host1.setHostAttributes(hostAttributes);
+    host2.setHostAttributes(hostAttributes);
+
+    host1.persist();
+    host2.persist();
 
-    host.persist();
     StackId stackId = new StackId("HDP-0.1");
     c1.setDesiredStackVersion(stackId);
-    helper.getOrCreateRepositoryVersion(stackId.getStackId(), "1.0-2086");
-    c1.createClusterVersion(stackId.getStackId(), "1.0-2086", "admin", RepositoryVersionState.CURRENT);
+    helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    c1.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
     clusters.mapHostToCluster("h1", "c1");
+    clusters.mapHostToCluster("h2", "c1");
     ClusterVersionDAOMock.failOnCurrentVersionState = false;
   }
 
@@ -186,10 +197,10 @@ public class ClusterTest {
 
   @Test
   public void testAddHost() throws AmbariException {
-    clusters.addHost("h2");
+    clusters.addHost("h3");
 
     try {
-      clusters.addHost("h2");
+      clusters.addHost("h3");
       fail("Duplicate add should fail");
     }
     catch (AmbariException e) {
@@ -544,20 +555,20 @@ public class ClusterTest {
     ClusterResponse r = c1.convertToResponse();
     Assert.assertEquals(c1.getClusterId(), r.getClusterId().longValue());
     Assert.assertEquals(c1.getClusterName(), r.getClusterName());
-    Assert.assertEquals(Integer.valueOf(1), r.getTotalHosts());
+    Assert.assertEquals(Integer.valueOf(2), r.getTotalHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getAlertStatusHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getHealthyStatusHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getUnhealthyStatusHosts());
-    Assert.assertEquals(1, r.getClusterHealthReport().getUnknownStatusHosts());
+    Assert.assertEquals(2, r.getClusterHealthReport().getUnknownStatusHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getStaleConfigsHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getMaintenanceStateHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getHealthyStateHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getHeartbeatLostStateHosts());
-    Assert.assertEquals(1, r.getClusterHealthReport().getInitStateHosts());
+    Assert.assertEquals(2, r.getClusterHealthReport().getInitStateHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getUnhealthyStateHosts());
 
-    clusters.addHost("h2");
-    Host host = clusters.getHost("h2");
+    clusters.addHost("h3");
+    Host host = clusters.getHost("h3");
     host.setIPv4("ipv4");
     host.setIPv6("ipv6");
 
@@ -570,20 +581,20 @@ public class ClusterTest {
     host.setStatus(host.getHealthStatus().getHealthStatus().name());
     host.persist();
     c1.setDesiredStackVersion(new StackId("HDP-2.0.6"));
-    clusters.mapHostToCluster("h2", "c1");
+    clusters.mapHostToCluster("h3", "c1");
 
     r = c1.convertToResponse();
 
-    Assert.assertEquals(Integer.valueOf(2), r.getTotalHosts());
+    Assert.assertEquals(Integer.valueOf(3), r.getTotalHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getAlertStatusHosts());
     Assert.assertEquals(1, r.getClusterHealthReport().getHealthyStatusHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getUnhealthyStatusHosts());
-    Assert.assertEquals(1, r.getClusterHealthReport().getUnknownStatusHosts());
+    Assert.assertEquals(2, r.getClusterHealthReport().getUnknownStatusHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getStaleConfigsHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getMaintenanceStateHosts());
     Assert.assertEquals(1, r.getClusterHealthReport().getHealthyStateHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getHeartbeatLostStateHosts());
-    Assert.assertEquals(1, r.getClusterHealthReport().getInitStateHosts());
+    Assert.assertEquals(2, r.getClusterHealthReport().getInitStateHosts());
     Assert.assertEquals(0, r.getClusterHealthReport().getUnhealthyStateHosts());
 
     // TODO write unit tests for debug dump
@@ -951,16 +962,20 @@ public class ClusterTest {
 
   @Test
   public void testTransitionClusterVersionTransactionFail() throws AmbariException {
-    ClusterVersionDAOMock.failOnCurrentVersionState = true;
     helper.getOrCreateRepositoryVersion("HDP", "0.2");
     c1.createClusterVersion("HDP", "0.2", "admin", RepositoryVersionState.INSTALLING);
     c1.transitionClusterVersion("HDP", "0.2", RepositoryVersionState.INSTALLED);
     c1.transitionClusterVersion("HDP", "0.2", RepositoryVersionState.UPGRADING);
     c1.transitionClusterVersion("HDP", "0.2", RepositoryVersionState.UPGRADED);
     try {
+      ClusterVersionDAOMock.failOnCurrentVersionState = true;
       c1.transitionClusterVersion("HDP", "0.2", RepositoryVersionState.CURRENT);
       Assert.fail();
-    } catch (AmbariException e) {}
+    } catch (AmbariException e) {
+
+    } finally {
+      ClusterVersionDAOMock.failOnCurrentVersionState = false;
+    }
 
     // There must be CURRENT state for cluster
     assertNotNull(c1.getCurrentClusterVersion());
@@ -1021,42 +1036,126 @@ public class ClusterTest {
   public void testRecalculateClusterVersionState() throws AmbariException {
     Host h1 = clusters.getHost("h1");
     h1.setState(HostState.HEALTHY);
+
+    Host h2 = clusters.getHost("h2");
+    h2.setState(HostState.HEALTHY);
+
+    // Phase 1: Install bits during distribution
     StackId stackId = new StackId("HDP-0.1");
+    final String stackVersion = "1.0-1000";
     RepositoryVersionEntity repositoryVersionEntity = helper.getOrCreateRepositoryVersion(stackId.getStackId(),
-        "1.0-1000");
-    c1.createClusterVersion(stackId.getStackId(), "1.0-1000", "admin", RepositoryVersionState.INSTALLING);
+        stackVersion);
+    // Because the cluster already has a Cluster Version, an additional stack must init with INSTALLING
+    c1.createClusterVersion(stackId.getStackId(), stackVersion, "admin", RepositoryVersionState.INSTALLING);
     c1.setCurrentStackVersion(stackId);
-    c1.recalculateClusterVersionState("1.0-1000");
-    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.OUT_OF_SYNC);
-
-    helper.createHostVersion("h1", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
-    c1.recalculateClusterVersionState("1.0-1000");
-    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.INSTALLING);
 
-    h1.setState(HostState.UNHEALTHY);
-    c1.recalculateClusterVersionState("1.0-1000");
-    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.OUT_OF_SYNC);
-
-    c1.recalculateClusterVersionState("1.0-2086");
-    checkStackVersionState(stackId.getStackId(), "1.0-2086", RepositoryVersionState.CURRENT);
+    HostVersionEntity hv1 = helper.createHostVersion("h1", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
+    HostVersionEntity hv2 = helper.createHostVersion("h2", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
+
+    c1.recalculateClusterVersionState(stackVersion);
+    //Should remain in its current state
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALLING);
+
+    h2.setState(HostState.UNHEALTHY);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.OUT_OF_SYNC);
+    // Retry by going back to INSTALLING
+    c1.transitionClusterVersion(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALLING);
+
+    h2.setState(HostState.HEALTHY);
+    hv2.setState(RepositoryVersionState.INSTALLED);
+    hostVersionDAO.merge(hv2);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALLING);
+
+    // Make one host fail
+    hv1.setState(RepositoryVersionState.INSTALL_FAILED);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALL_FAILED);
+    // Retry by going back to INSTALLING
+    c1.transitionClusterVersion(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALLING);
+
+    // Now, all hosts are in INSTALLED
+    hv1.setState(RepositoryVersionState.INSTALLED);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.INSTALLED);
+
+    // Phase 2: Upgrade stack
+    hv1.setState(RepositoryVersionState.UPGRADING);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADING);
+
+    hv2.setState(RepositoryVersionState.UPGRADING);
+    hostVersionDAO.merge(hv2);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADING);
+
+    hv2.setState(RepositoryVersionState.UPGRADE_FAILED);
+    hostVersionDAO.merge(hv2);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADE_FAILED);
+    // Retry by going back to UPGRADING
+    c1.transitionClusterVersion(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADING);
+
+    hv2.setState(RepositoryVersionState.UPGRADED);
+    hostVersionDAO.merge(hv2);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADING);
+
+    // Now both hosts are UPGRADED
+    hv1.setState(RepositoryVersionState.UPGRADED);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.UPGRADED);
+
+    // Set both hosts to CURRENT
+    hv1.setState(RepositoryVersionState.CURRENT);
+    hostVersionDAO.merge(hv1);
+    hv2.setState(RepositoryVersionState.CURRENT);
+    hostVersionDAO.merge(hv2);
+    c1.recalculateClusterVersionState(stackVersion);
+    checkStackVersionState(stackId.getStackId(), stackVersion, RepositoryVersionState.CURRENT);
   }
 
   @Test
   public void testRecalculateAllClusterVersionStates() throws AmbariException {
     Host h1 = clusters.getHost("h1");
     h1.setState(HostState.HEALTHY);
+
+    Host h2 = clusters.getHost("h2");
+    h2.setState(HostState.HEALTHY);
+
     StackId stackId = new StackId("HDP-0.1");
     RepositoryVersionEntity repositoryVersionEntity = helper.getOrCreateRepositoryVersion(stackId.getStackId(),
         "1.0-1000");
     c1.createClusterVersion(stackId.getStackId(), "1.0-1000", "admin", RepositoryVersionState.INSTALLING);
     c1.setCurrentStackVersion(stackId);
     c1.recalculateAllClusterVersionStates();
-    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.OUT_OF_SYNC);
+    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.INSTALLING);
     checkStackVersionState(stackId.getStackId(), "1.0-2086", RepositoryVersionState.CURRENT);
 
-    helper.createHostVersion("h1", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
+    HostVersionEntity hv1 = helper.createHostVersion("h1", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
+    HostVersionEntity hv2 = helper.createHostVersion("h2", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
+
     c1.recalculateAllClusterVersionStates();
     checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.INSTALLING);
     checkStackVersionState(stackId.getStackId(), "1.0-2086", RepositoryVersionState.CURRENT);
+
+    hv1.setState(RepositoryVersionState.INSTALL_FAILED);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateAllClusterVersionStates();
+    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.INSTALL_FAILED);
+    checkStackVersionState(stackId.getStackId(), "1.0-2086", RepositoryVersionState.CURRENT);
+    // Retry by going back to INSTALLING
+    c1.transitionClusterVersion(stackId.getStackId(), "1.0-1000", RepositoryVersionState.INSTALLING);
+
+    hv1.setState(RepositoryVersionState.CURRENT);
+    hostVersionDAO.merge(hv1);
+    c1.recalculateAllClusterVersionStates();
+    checkStackVersionState(stackId.getStackId(), "1.0-1000", RepositoryVersionState.OUT_OF_SYNC);
+    checkStackVersionState(stackId.getStackId(), "1.0-2086", RepositoryVersionState.CURRENT);
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
index 30158cc..ebf742e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
@@ -226,9 +226,11 @@ public class ClustersTest {
     StackId stackId = new StackId("HDP-0.1");
     cluster1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster1.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
     cluster2.setDesiredStackVersion(stackId);
-    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster2.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     try {
       clusters.mapHostToCluster(h1, c1);
@@ -312,9 +314,11 @@ public class ClustersTest {
     StackId stackId = new StackId("HDP-0.1");
     cluster1.setDesiredStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster1.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
     cluster2.setDesiredStackVersion(stackId);
-    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster2.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
     clusters.addHost(h1);
     clusters.addHost(h2);
     clusters.addHost(h3);
@@ -345,7 +349,8 @@ public class ClustersTest {
     cluster.setDesiredStackVersion(stackId);
     cluster.setCurrentStackVersion(stackId);
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
+    cluster.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(), RepositoryVersionState.CURRENT);
 
     final Config config1 = injector.getInstance(ConfigFactory.class).createNew(cluster, "t1",
         new HashMap<String, String>() {{

http://git-wip-us.apache.org/repos/asf/ambari/blob/aab897d5/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
index 2aac5f4..cac7dd7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
@@ -364,7 +364,7 @@ public class HostTest {
     Cluster c1 = clusters.getCluster("c1");
     StackId stackId = new StackId("HDP-0.1");
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     Assert.assertEquals("c1", c1.getClusterName());
     Assert.assertEquals(1, c1.getClusterId());
     clusters.addHost("h1");
@@ -439,7 +439,7 @@ public class HostTest {
     host.persist();
     StackId stackId = new StackId("HDP-0.1");
     helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
-    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.UPGRADING);
     c1.setDesiredStackVersion(stackId);
     clusters.mapHostToCluster("h1", "c1");