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 2014/11/20 22:37:11 UTC

[2/2] ambari git commit: AMBARI-8149. Rolling Upgrade Bootstrap - Allow for adding Host, needs to populate new tables (alejandro)

AMBARI-8149. Rolling Upgrade Bootstrap - Allow for adding Host, needs to populate new tables (alejandro)


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

Branch: refs/heads/trunk
Commit: c938c4840c8ecd22e01ce923a6ededa069830802
Parents: 9a87525
Author: Alejandro Fernandez <af...@hortonworks.com>
Authored: Mon Nov 17 18:01:36 2014 -0800
Committer: Alejandro Fernandez <af...@hortonworks.com>
Committed: Thu Nov 20 13:37:01 2014 -0800

----------------------------------------------------------------------
 .../AmbariManagementControllerImpl.java         |   6 +
 .../internal/HostResourceProvider.java          |  26 ++-
 .../server/orm/dao/ClusterVersionDAO.java       |  13 +-
 .../ambari/server/orm/dao/HostVersionDAO.java   |  18 +-
 .../org/apache/ambari/server/state/Cluster.java |  49 ++++-
 .../org/apache/ambari/server/state/Host.java    |   7 +
 .../org/apache/ambari/server/state/StackId.java |   5 +-
 .../server/state/cluster/ClusterImpl.java       | 194 +++++++++++++++++++
 .../server/state/cluster/ClustersImpl.java      | 129 +++++++-----
 .../ambari/server/state/host/HostImpl.java      |  13 ++
 .../ambari/server/utils/VersionUtils.java       |  10 +-
 .../server/agent/TestHeartbeatHandler.java      |   3 +-
 .../server/agent/TestHeartbeatMonitor.java      |  22 ++-
 .../AmbariManagementControllerTest.java         |  43 +++-
 ...hYarnCapacitySchedulerReleaseConfigTest.java |  15 +-
 .../StackDefinedPropertyProviderTest.java       |   5 +-
 .../internal/UpgradeResourceProviderTest.java   |   5 +-
 .../apache/ambari/server/events/EventsTest.java |   5 +-
 .../server/orm/dao/AlertDispatchDAOTest.java    |   5 +-
 .../ambari/server/orm/dao/AlertsDAOTest.java    |   5 +-
 .../server/orm/dao/ClusterVersionDAOTest.java   |  20 +-
 .../server/orm/dao/HostVersionDAOTest.java      |  34 ++--
 .../server/state/ServiceComponentTest.java      |   7 +-
 .../server/state/cluster/ClusterTest.java       |   5 +-
 .../server/state/cluster/ClustersTest.java      |  32 ++-
 .../ambari/server/state/host/HostTest.java      |   9 +-
 .../svccomphost/ServiceComponentHostTest.java   |  26 ++-
 .../ambari/server/utils/TestVersionUtils.java   |  18 ++
 28 files changed, 575 insertions(+), 154 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 5c286ea..9d3a1f8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -103,6 +103,7 @@ import org.apache.ambari.server.security.ldap.LdapSyncDto;
 import org.apache.ambari.server.stageplanner.RoleGraph;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.CommandScriptDefinition;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.Config;
@@ -360,6 +361,9 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       StackId newStackId = new StackId(request.getStackVersion());
       c.setDesiredStackVersion(newStackId);
       clusters.setCurrentStackVersion(request.getClusterName(), newStackId);
+      // TODO Alejandro, this needs to use the repo version, e.g., 2.2.0.0.-994
+      // For the sake of having the end-to-end workflow work, use as is.
+      c.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), getAuthName(), RepositoryVersionState.CURRENT);
     }
 
     if (request.getHostNames() != null) {
@@ -1225,6 +1229,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     if (currentVersion == null) {
       cluster.setCurrentStackVersion(desiredVersion);
     }
+    // Rolling Upgrade: unlike the workflow for creating a cluster, updating a cluster via the API will not
+    // create any ClusterVersionEntity changes because those have to go through the Rolling Upgrade process.
 
     boolean requiresHostListUpdate =
         request.getHostNames() != null && !request.getHostNames().isEmpty();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 0b5f746..c53eb78 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -339,6 +339,12 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   }
 
 
+  /**
+   * Accepts a request with registered hosts and if the request contains a cluster name then will map all of the
+   * hosts onto that cluster.
+   * @param requests Request that must contain registered hosts, and optionally a cluster.
+   * @throws AmbariException
+   */
   protected synchronized void createHosts(Set<HostRequest> requests)
       throws AmbariException {
 
@@ -426,7 +432,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     Map<String, Set<String>> hostClustersMap = new HashMap<String, Set<String>>();
     Map<String, Map<String, String>> hostAttributes = new HashMap<String, Map<String, String>>();
     for (HostRequest request : requests) {
-      if (request.getHostname() != null) {
+      if (request.getHostname() != null && !request.getHostname().isEmpty() && request.getClusterName() != null && !request.getClusterName().isEmpty()) {
         Set<String> clusterSet = new HashSet<String>();
         clusterSet.add(request.getClusterName());
         hostClustersMap.put(request.getHostname(), clusterSet);
@@ -538,14 +544,15 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     // We don't expect batch requests for different clusters, that's why
     // nothing bad should happen if value is overwritten few times
     String maintenanceCluster = null;
-    
+
     for (HostRequest request : requests) {
-      if (request.getHostname() == null
-          || request.getHostname().isEmpty()) {
-        throw new IllegalArgumentException("Invalid arguments, hostname should"
-            + " be provided");
+      if (request.getHostname() == null || request.getHostname().isEmpty()) {
+        throw new IllegalArgumentException("Invalid arguments, hostname should be provided");
       }
+    }
 
+
+    for (HostRequest request : requests) {
       if (LOG.isDebugEnabled()) {
         LOG.debug("Received a updateHost request"
             + ", hostname=" + request.getHostname()
@@ -555,10 +562,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
       Host h = clusters.getHost(request.getHostname());
 
       try {
-        //todo: the below method throws an exception when trying to create a duplicate mapping.
-        //todo: this is done to detect duplicates during host create.  Unless it is allowable to
-        //todo: add a host to a cluster by modifying the cluster_name prop, we should not do this mapping here.
-        //todo: Determine if it is allowable to associate a host to a cluster via this mechanism.
+        // The below method call throws an exception when trying to create a duplicate mapping in the clusterhostmapping
+        // table. This is done to detect duplicates during host create. In order to be robust, handle these gracefully.
         clusters.mapHostToCluster(request.getHostname(), request.getClusterName());
       } catch (DuplicateResourceException e) {
         // do nothing
@@ -592,6 +597,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
         }
       }
 
+      // Create configurations
       if (null != request.getClusterName() && null != request.getDesiredConfigs()) {
         Cluster c = clusters.getCluster(request.getClusterName());
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterVersionDAO.java
index 7202373..1ce4a73 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterVersionDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterVersionDAO.java
@@ -31,9 +31,9 @@ import com.google.inject.Singleton;
 
 /**
  * The {@link ClusterVersionDAO} class manages the {@link ClusterVersionEntity} instances associated with a cluster.
- * Each cluster can have multiple stack versions {@link RepositoryVersionState#INSTALLED},
- * exactly one stack version that is {@link RepositoryVersionState#CURRENT}, and at most one
- * stack version that is {@link RepositoryVersionState#UPGRADING}.
+ * Each cluster can have multiple stack versions {@link org.apache.ambari.server.state.RepositoryVersionState#INSTALLED},
+ * exactly one stack version that is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, and at most one
+ * stack version that is {@link org.apache.ambari.server.state.RepositoryVersionState#UPGRADING}.
  */
 @Singleton
 public class ClusterVersionDAO extends CrudDAO<ClusterVersionEntity, Long>{
@@ -80,11 +80,12 @@ public class ClusterVersionDAO extends CrudDAO<ClusterVersionEntity, Long>{
   }
 
   /**
-   * Get the cluster version for the given cluster name.
+   * Retrieve all of the cluster versions for the given cluster.
    *
    * @param clusterName Cluster name
    * @return Return all of the cluster versions associated with the given cluster.
    */
+  @RequiresSession
   public List<ClusterVersionEntity> findByCluster(String  clusterName) {
     final TypedQuery<ClusterVersionEntity> query = entityManagerProvider.get()
         .createNamedQuery("clusterVersionByCluster", ClusterVersionEntity.class);
@@ -94,11 +95,11 @@ public class ClusterVersionDAO extends CrudDAO<ClusterVersionEntity, Long>{
   }
 
   /**
-   * Retrieve the single cluster version whose state is {@link RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
+   * Retrieve the single cluster version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
    * for the given cluster.
    *
    * @param clusterName Cluster name
-   * @return Returns the single cluster version for this cluster whose state is {@link RepositoryVersionState#CURRENT}, or {@code null} otherwise.
+   * @return Returns the single cluster version for this cluster whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, or {@code null} otherwise.
    */
   @RequiresSession
   public ClusterVersionEntity findByClusterAndStateCurrent(String clusterName) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
index 20648a1..1afc850 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
@@ -35,7 +35,7 @@ import java.util.List;
  * The {@link org.apache.ambari.server.orm.dao.HostVersionDAO} class manages the {@link org.apache.ambari.server.orm.entities.HostVersionEntity}
  * instances associated with a host. Each host can have multiple stack versions in {@link org.apache.ambari.server.state.RepositoryVersionState#INSTALLED}
  * which are installed, exactly one stack version that is either {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT} or
- * {@link org.apache.ambari.server.state.RepositoryVersionState.#UPGRADING}.
+ * {@link org.apache.ambari.server.state.RepositoryVersionState#UPGRADING}.
  */
 @Singleton
 public class HostVersionDAO {
@@ -74,32 +74,32 @@ public class HostVersionDAO {
   }
 
   /**
-   * Retrieve all of the host versions for the given cluster name and host name.
+   * Retrieve all of the host versions for the given host name across all clusters.
    *
-   * @param clusterName Cluster name
    * @param hostName FQDN of host
    * @return Return all of the host versions that match the criteria.
    */
   @RequiresSession
-  public List<HostVersionEntity> findByClusterAndHost(String  clusterName, String hostName) {
+  public List<HostVersionEntity> findByHost(String hostName) {
     final TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
-        .createNamedQuery("hostVersionByClusterAndHostname", HostVersionEntity.class);
-    query.setParameter("clusterName", clusterName);
+        .createNamedQuery("hostVersionByHostname", HostVersionEntity.class);
     query.setParameter("hostName", hostName);
 
     return daoUtils.selectList(query);
   }
 
   /**
-   * Retrieve all of the host versions for the given and host name.
+   * Retrieve all of the host versions for the given cluster name and host name.
    *
+   * @param clusterName Cluster name
    * @param hostName FQDN of host
    * @return Return all of the host versions that match the criteria.
    */
   @RequiresSession
-  public List<HostVersionEntity> findByHost(String hostName) {
+  public List<HostVersionEntity> findByClusterAndHost(String  clusterName, String hostName) {
     final TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
-        .createNamedQuery("hostVersionByHostname", HostVersionEntity.class);
+        .createNamedQuery("hostVersionByClusterAndHostname", HostVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
     query.setParameter("hostName", hostName);
 
     return daoUtils.selectList(query);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 204e1d9..945a4db 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -28,6 +28,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.scheduler.RequestExecution;
 
@@ -81,8 +82,20 @@ public interface Cluster {
    * @param svcCompHost
    */  
   public void removeServiceComponentHost(ServiceComponentHost svcCompHost) throws AmbariException;
-  
-  
+
+
+  /**
+   * Get the ClusterVersionEntity object whose state is CURRENT.
+   * @return
+   */
+  public ClusterVersionEntity getCurrentClusterVersion();
+
+  /**
+   * Get all of the ClusterVersionEntity objects for the cluster.
+   * @return
+   */
+  public Collection<ClusterVersionEntity> getAllClusterVersions();
+
   /**
    * Get desired stack version
    * @return
@@ -106,7 +119,37 @@ public interface Cluster {
    * @param stackVersion
    */
   public void setCurrentStackVersion(StackId stackVersion) throws AmbariException;
-  
+
+  /**
+   * Create host versions for all of the hosts with the applied desired state using the cluster's current stack version.
+   * @param hostNames Collection of host names
+   * @param desiredState Desired state must be {@link RepositoryVersionState#CURRENT} or {@link RepositoryVersionState#UPGRADING}
+   * @throws AmbariException
+   */
+  public void mapHostVersions(Set<String> hostNames, ClusterVersionEntity currentClusterVersion, RepositoryVersionState desiredState) throws AmbariException;
+
+  /**
+   * Create a cluster version for the given stack and version, whose initial state must either
+   * be either {@link RepositoryVersionState#CURRENT} (if no other cluster version exists) or
+   * {@link RepositoryVersionState#UPGRADING} (if at exactly one CURRENT cluster version already exists).
+   * @param stack Stack name
+   * @param version Stack version
+   * @param userName User performing the operation
+   * @param state Initial state
+   * @throws AmbariException
+   */
+  public void createClusterVersion(String stack, String version, String userName, RepositoryVersionState state) throws AmbariException;
+
+
+  /**
+   * Transition an existing cluster version from one state to another.
+   * @param stack Stack name
+   * @param version Stack version
+   * @param state Desired state
+   * @throws AmbariException
+   */
+  public void transitionClusterVersion(String stack, String version, RepositoryVersionState state) throws AmbariException;
+
   /**
    * Gets whether the cluster is still initializing or has finished with its
    * deployment requests.

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
index fcbec6b..216e3d1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
@@ -26,6 +26,7 @@ import org.apache.ambari.server.agent.AgentEnv;
 import org.apache.ambari.server.agent.DiskInfo;
 import org.apache.ambari.server.agent.HostInfo;
 import org.apache.ambari.server.controller.HostResponse;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 
 public interface Host {
@@ -365,4 +366,10 @@ public interface Host {
    * @return the maintenance state
    */
   public MaintenanceState getMaintenanceState(long clusterId);
+
+  /**
+   * Get all of the HostVersionEntity objects for the host.
+   * @return
+   */
+  public List<HostVersionEntity> getAllHostVersions();
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/state/StackId.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/StackId.java b/ambari-server/src/main/java/org/apache/ambari/server/state/StackId.java
index edfd6dc..0ac1ddb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/StackId.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/StackId.java
@@ -86,12 +86,13 @@ public class StackId implements Comparable<StackId> {
       return true;
     }
     StackId s = (StackId) object;
-    return stackVersion.equals(s.stackVersion);
+    return stackName.equals(s.stackName) && stackVersion.equals(s.stackVersion);
   }
 
   @Override
   public int hashCode() {
-    int result = stackVersion != null ? stackVersion.hashCode() : 0;
+    int result = stackName != null ? stackName.hashCode() : 0;
+    result = 31 * result + (stackVersion != null ? stackVersion.hashCode() : 0);
     return result;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 2b84d0e..e3f6d4b 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
@@ -37,6 +37,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 import javax.persistence.RollbackException;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.DuplicateResourceException;
 import org.apache.ambari.server.ConfigGroupNotFoundException;
 import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.ParentObjectNotFoundException;
@@ -54,16 +55,22 @@ import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.dao.ClusterStateDAO;
+import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
 import org.apache.ambari.server.orm.dao.HostConfigMappingDAO;
+import org.apache.ambari.server.orm.dao.HostDAO;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.ServiceConfigDAO;
 import org.apache.ambari.server.orm.dao.UpgradeDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.ClusterStateEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupEntity;
+import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.RequestScheduleEntity;
@@ -73,6 +80,7 @@ import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ClusterHealthReport;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
 import org.apache.ambari.server.state.ConfigHelper;
@@ -90,6 +98,7 @@ import org.apache.ambari.server.state.ServiceFactory;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.UpgradeState;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
@@ -99,6 +108,7 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.Sets;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Multimap;
@@ -166,6 +176,12 @@ public class ClusterImpl implements Cluster {
   @Inject
   private ClusterStateDAO clusterStateDAO;
   @Inject
+  private ClusterVersionDAO clusterVersionDAO;
+  @Inject
+  private HostDAO hostDAO;
+  @Inject
+  private HostVersionDAO hostVersionDAO;
+  @Inject
   private ServiceFactory serviceFactory;
   @Inject
   private ConfigFactory configFactory;
@@ -1077,6 +1093,184 @@ public class ClusterImpl implements Cluster {
     }
   }
 
+  /**
+   * Get the ClusterVersionEntity object whose state is CURRENT.
+   * @return
+   */
+  @Override
+  public ClusterVersionEntity getCurrentClusterVersion() {
+    return clusterVersionDAO.findByClusterAndStateCurrent(this.getClusterName());
+  }
+
+  /**
+   * Get all of the ClusterVersionEntity objects for the cluster.
+   * @return
+   */
+  @Override
+  public Collection<ClusterVersionEntity> getAllClusterVersions() {
+    return clusterVersionDAO.findByCluster(this.getClusterName());
+  }
+
+  /**
+   * Create host versions for all of the hosts that don't already have the stack version.
+   * @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) {
+      throw new AmbariException("Could not find current stack version of cluster " + this.getClusterName());
+    }
+
+    final Set<RepositoryVersionState> validStates = new HashSet<RepositoryVersionState>(){{
+      add(RepositoryVersionState.CURRENT);
+      add(RepositoryVersionState.UPGRADING);
+    }};
+
+    if (!validStates.contains(desiredState)) {
+      throw new AmbariException("The state must be one of [" + StringUtils.join(validStates, ", ") + "]");
+    }
+
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+        Set<String> existingHostsWithClusterStackAndVersion = new HashSet<String>();
+        List<HostVersionEntity> existingHostVersionEntities = hostVersionDAO.findByClusterStackAndVersion(this.getClusterName(), currentClusterVersion.getStack(), currentClusterVersion.getVersion());
+        if (existingHostVersionEntities != null) {
+          for (HostVersionEntity entity : existingHostVersionEntities) {
+            existingHostsWithClusterStackAndVersion.add(entity.getHostName());
+          }
+        }
+
+        Sets.SetView<String> intersection = Sets.intersection(existingHostsWithClusterStackAndVersion, hostNames);
+
+        for (String hostname : hostNames) {
+          // Notice that if any hosts already have the desired stack and version, regardless of the state, we try
+          // to be robust and only insert records for the missing hosts.
+          if (!intersection.contains(hostname)) {
+            HostEntity hostEntity = hostDAO.findByName(hostname);
+            HostVersionEntity hostVersionEntity = new HostVersionEntity(hostname, currentClusterVersion.getStack(), currentClusterVersion.getVersion(), desiredState);
+            hostVersionEntity.setHostEntity(hostEntity);
+            hostVersionDAO.create(hostVersionEntity);
+          }
+        }
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  /**
+   * Create a cluster version for the given stack and version, whose initial state must either
+   * be either {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT} (if no other cluster version exists) or
+   * {@link org.apache.ambari.server.state.RepositoryVersionState#UPGRADING} (if at exactly one CURRENT cluster version already exists).
+   * @param stack Stack name
+   * @param version Stack version
+   * @param userName User performing the operation
+   * @param state Initial state
+   * @throws AmbariException
+   */
+  @Override
+  public void createClusterVersion(String stack, String version, String userName, RepositoryVersionState state) throws AmbariException {
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+        RepositoryVersionState allowedState;
+        if (this.clusterEntity.getClusterVersionEntities() == null || this.clusterEntity.getClusterVersionEntities().isEmpty()) {
+          allowedState = RepositoryVersionState.CURRENT;
+        } else {
+          allowedState = RepositoryVersionState.UPGRADING;
+        }
+
+        if (allowedState != state) {
+          throw new AmbariException("The allowed state for a new cluster version must be " + allowedState);
+        }
+
+        ClusterVersionEntity existing = clusterVersionDAO.findByClusterAndStackAndVersion(this.getClusterName(), stack, version);
+        if (existing != null) {
+          throw new DuplicateResourceException("Duplicate item, a cluster version with stack=" + stack + ", version=" +
+              version + " for cluster " + this.getClusterName() + " already exists");
+        }
+
+        ClusterVersionEntity clusterVersionEntity = new ClusterVersionEntity(this.clusterEntity, stack, version, state, System.currentTimeMillis(), System.currentTimeMillis(), userName);
+        clusterVersionDAO.create(clusterVersionEntity);
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  /**
+   * Transition an existing cluster version from one state to another.
+   * @param stack Stack name
+   * @param version Stack version
+   * @param state Desired state
+   * @throws AmbariException
+   */
+  @Override
+  public void transitionClusterVersion(String stack, String version, RepositoryVersionState state) throws AmbariException {
+    Set<RepositoryVersionState> allowedStates = new HashSet<RepositoryVersionState>();
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+        ClusterVersionEntity existingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(this.getClusterName(), stack, version);
+        if (existingClusterVersion == null) {
+          throw new AmbariException("Existing cluster version not found for cluster=" + this.getClusterName() + ", stack=" + stack + ", version=" + version);
+        }
+
+        if (existingClusterVersion.getState() != state) {
+          switch (existingClusterVersion.getState()) {
+            case CURRENT:
+              allowedStates.add(RepositoryVersionState.INSTALLED);
+            case INSTALLED:
+              allowedStates.add(RepositoryVersionState.CURRENT);
+            case UPGRADING:
+              allowedStates.add(RepositoryVersionState.CURRENT);
+              allowedStates.add(RepositoryVersionState.UPGRADE_FAILED);
+            case UPGRADE_FAILED:
+              allowedStates.add(RepositoryVersionState.UPGRADING);
+          }
+
+          if (!allowedStates.contains(state)) {
+            throw new AmbariException("Invalid cluster version transition from " + existingClusterVersion.getState() + " to " + state);
+          }
+
+          // There must be exactly one cluster version whose state is CURRENT at all times.
+          if (state == RepositoryVersionState.CURRENT) {
+            ClusterVersionEntity currentVersion = clusterVersionDAO.findByClusterAndStateCurrent(this.getClusterName());
+            if (currentVersion == null) {
+              throw new AmbariException("Unable to find CURRENT cluster version for cluster " + this.getClusterName());
+            }
+
+            currentVersion.setState(RepositoryVersionState.INSTALLED);
+            clusterVersionDAO.merge(currentVersion);
+          }
+
+          existingClusterVersion.setState(state);
+          existingClusterVersion.setEndTime(System.currentTimeMillis());
+          clusterVersionDAO.merge(existingClusterVersion);
+        }
+      } catch (RollbackException e) {
+        String message = "Unable to transition stack " + stack + " at version " + version + " for cluster " + getClusterName() + " to state " + state;
+        LOG.warn(message);
+        throw new AmbariException(message, e);
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
   @Override
   public void setCurrentStackVersion(StackId stackVersion)
     throws AmbariException {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
index 4cfa308..9cdb498 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
@@ -40,12 +40,16 @@ import org.apache.ambari.server.agent.DiskInfo;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
+import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.ResourceDAO;
 import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
+import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.ResourceEntity;
@@ -60,6 +64,7 @@ import org.apache.ambari.server.state.HostHealthStatus;
 import org.apache.ambari.server.state.HostHealthStatus.HealthStatus;
 import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.RepositoryInfo;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.host.HostFactory;
@@ -67,6 +72,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.GrantedAuthority;
 
+import com.google.common.collect.Sets;
 import com.google.gson.Gson;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -95,6 +101,10 @@ public class ClustersImpl implements Clusters {
   @Inject
   HostDAO hostDAO;
   @Inject
+  ClusterVersionDAO clusterVersionDAO;
+  @Inject
+  HostVersionDAO hostVersionDAO;
+  @Inject
   ResourceDAO resourceDAO;
   @Inject
   ResourceTypeDAO resourceTypeDAO;
@@ -314,6 +324,12 @@ public class ClustersImpl implements Clusters {
     }
   }
 
+  /**
+   * Register a host by creating a {@link HostEntity} object in the database and setting its state to
+   * {@link HostState#INIT}. This does not add the host the cluster.
+   * @param hostname Host to be added
+   * @throws AmbariException
+   */
   @Override
   public void addHost(String hostname) throws AmbariException {
     checkLoaded();
@@ -377,42 +393,13 @@ public class ClustersImpl implements Clusters {
           if (attributes != null && !attributes.isEmpty()){
             host.setHostAttributes(attributes);
           }
+          host.refresh();
+
 
           Set<String> hostClusterNames = hostClusters.get(hostname);
           for (String clusterName : hostClusterNames) {
             if (clusterName != null && !clusterName.isEmpty()) {
-              Cluster cluster = clusterMap.get(clusterName);
-
-              for (Cluster c : hostClusterMap.get(hostname)) {
-                if (c.getClusterName().equals(clusterName)) {
-                  throw new DuplicateResourceException("Attempted to create a host which already exists: clusterName=" +
-                      clusterName + ", hostName=" + hostname);
-                }
-              }
-
-              if (!isOsSupportedByClusterStack(cluster, host)) {
-                String message = "Trying to map host to cluster where stack does not"
-                    + " support host's os type"
-                    + ", clusterName=" + clusterName
-                    + ", clusterStackId=" + cluster.getDesiredStackVersion().getStackId()
-                    + ", hostname=" + hostname
-                    + ", hostOsFamily=" + host.getOsFamily();
-                LOG.warn(message);
-                throw new AmbariException(message);
-              }
-
-              mapHostClusterEntities(hostname, cluster.getClusterId());
-              host.refresh();
-              cluster.refresh();
-              hostClusterMap.get(hostname).add(cluster);
-              clusterHostMap.get(clusterName).add(host);
-
-              if (LOG.isDebugEnabled()) {
-                LOG.debug("Mapping a host to a cluster"
-                    + ", clusterName=" + clusterName
-                    + ", clusterId=" + cluster.getClusterId()
-                    + ", hostname=" + hostname);
-              }
+              mapHostToCluster(hostname, clusterName);
             }
           }
         }
@@ -462,9 +449,35 @@ public class ClustersImpl implements Clusters {
     return clusterMap;
   }
 
+  /**
+   *  For each host, attempts to map it to the cluster, and apply the cluster's current version to the host.
+   * @param hostnames Collection of host names
+   * @param clusterName Cluster name
+   * @throws AmbariException
+   */
   @Override
-  public void mapHostToCluster(String hostname,
-                               String clusterName) throws AmbariException {
+  public void mapHostsToCluster(Set<String> hostnames, String clusterName) throws AmbariException {
+    checkLoaded();
+    w.lock();
+    try {
+      ClusterVersionEntity clusterVersionEntity = clusterVersionDAO.findByClusterAndStateCurrent(clusterName);
+      for (String hostname : hostnames) {
+        mapHostToCluster(hostname, clusterName, clusterVersionEntity);
+      }
+    } finally {
+      w.unlock();
+    }
+  }
+
+  /**
+   * Attempts to map the host to the cluster via clusterhostmapping table if not already present, and add a host_version
+   * record for the cluster's currently applied (stack, version) if not already present.
+   * @param hostname Host name
+   * @param clusterName Cluster name
+   * @param currentClusterVersion Cluster's current stack version
+   * @throws AmbariException May throw a DuplicateResourceException.
+   */
+  public void mapHostToCluster(String hostname, String clusterName, ClusterVersionEntity currentClusterVersion) throws AmbariException {
     checkLoaded();
     w.lock();
 
@@ -491,6 +504,8 @@ public class ClustersImpl implements Clusters {
       }
 
       mapHostClusterEntities(hostname, cluster.getClusterId());
+      cluster.mapHostVersions(Sets.newHashSet(hostname), currentClusterVersion, RepositoryVersionState.CURRENT);
+
       host.refresh();
       cluster.refresh();
       hostClusterMap.get(hostname).add(cluster);
@@ -507,8 +522,24 @@ public class ClustersImpl implements Clusters {
     }
   }
 
+  /**
+   * Attempts to map the host to the cluster via clusterhostmapping table if not already present, and add a host_version
+   * record for the cluster's currently applied (stack, version) if not already present. This function is overloaded.
+   * @param hostname Host name
+   * @param clusterName Cluster name
+   * @throws AmbariException May throw a DuplicateResourceException.
+   */
+  @Override
+  public void mapHostToCluster(String hostname,
+                               String clusterName) throws AmbariException {
+    checkLoaded();
+
+    ClusterVersionEntity clusterVersionEntity = clusterVersionDAO.findByClusterAndStateCurrent(clusterName);
+    mapHostToCluster(hostname, clusterName, clusterVersionEntity);
+  }
+
   @Transactional
-  void mapHostClusterEntities(String hostName, Long clusterId) {
+  private void mapHostClusterEntities(String hostName, Long clusterId) {
     HostEntity hostEntity = hostDAO.findByName(hostName);
     ClusterEntity clusterEntity = clusterDAO.findById(clusterId);
 
@@ -532,20 +563,6 @@ public class ClustersImpl implements Clusters {
   }
 
   @Override
-  public void mapHostsToCluster(Set<String> hostnames,
-                                             String clusterName) throws AmbariException {
-    checkLoaded();
-    w.lock();
-    try {
-      for (String hostname : hostnames) {
-        mapHostToCluster(hostname, clusterName);
-      }
-    } finally {
-      w.unlock();
-    }
-  }
-
-  @Override
   public void updateClusterName(String oldName, String newName) {
     w.lock();
     try {
@@ -617,6 +634,12 @@ public class ClustersImpl implements Clusters {
         clusterSet.remove(cluster);
       }
       clusterHostMap.remove(cluster.getClusterName());
+
+      Collection<ClusterVersionEntity> clusterVersions = cluster.getAllClusterVersions();
+      for (ClusterVersionEntity clusterVersion : clusterVersions) {
+        clusterVersionDAO.remove(clusterVersion);
+      }
+
       clusters.remove(clusterName);
     } finally {
       w.unlock();
@@ -691,11 +714,17 @@ public class ClustersImpl implements Clusters {
     w.lock();
 
     try {
+      deleteConfigGroupHostMapping(hostname);
+
+      Collection<HostVersionEntity> hostVersions = hosts.get(hostname).getAllHostVersions();
+      for (HostVersionEntity hostVersion : hostVersions) {
+        hostVersionDAO.remove(hostVersion);
+      }
+
       HostEntity entity = hostDAO.findByName(hostname);
       hostDAO.refresh(entity);
       hostDAO.remove(entity);
       hosts.remove(hostname);
-      deleteConfigGroupHostMapping(hostname);
     } catch (Exception e) {
       throw new AmbariException("Could not remove host", e);
     } finally {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
index 9ba38eb..2a49e24 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
@@ -39,9 +39,11 @@ import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.dao.HostConfigMappingDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.HostStateDAO;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostStateEntity;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -107,6 +109,7 @@ public class HostImpl implements Host {
   private HostStateEntity hostStateEntity;
   private HostDAO hostDAO;
   private HostStateDAO hostStateDAO;
+  private HostVersionDAO hostVersionDAO;
   private ClusterDAO clusterDAO;
   private Clusters clusters;
   private HostConfigMappingDAO hostConfigMappingDAO;
@@ -229,6 +232,7 @@ public class HostImpl implements Host {
     this.persisted = persisted;
     hostDAO = injector.getInstance(HostDAO.class);
     hostStateDAO = injector.getInstance(HostStateDAO.class);
+    hostVersionDAO = injector.getInstance(HostVersionDAO.class);
     gson = injector.getInstance(Gson.class);
     clusterDAO = injector.getInstance(ClusterDAO.class);
     clusters = injector.getInstance(Clusters.class);
@@ -1317,6 +1321,15 @@ public class HostImpl implements Host {
     }
   }
 
+  /**
+   * Get all of the HostVersionEntity objects for the host.
+   * @return
+   */
+  @Override
+  public List<HostVersionEntity> getAllHostVersions() {
+    return hostVersionDAO.findByHost(this.getHostName());
+  }
+
 }
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/main/java/org/apache/ambari/server/utils/VersionUtils.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/VersionUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/VersionUtils.java
index a327292..8796bd3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/utils/VersionUtils.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/VersionUtils.java
@@ -27,7 +27,7 @@ import org.apache.commons.lang.StringUtils;
  */
 public class VersionUtils {
   /**
-   * Compares two versions strings of the form N.N.N.N
+   * Compares two versions strings of the form N.N.N.N or even N.N.N.N-### (which should ignore everything after the dash).
    *
    * @param version1
    * @param version2
@@ -40,6 +40,13 @@ public class VersionUtils {
     version1 = StringUtils.trim(version1);
     version2 = StringUtils.trim(version2);
 
+    if (version1.indexOf('-') >=0) {
+      version1 = version1.substring(0, version1.indexOf('-'));
+    }
+    if (version2.indexOf('-') >=0) {
+      version2 = version2.substring(0, version2.indexOf('-'));
+    }
+
     if (version1 == null || version1.isEmpty()) {
       throw new IllegalArgumentException("version1 cannot be null or empty");
     }
@@ -50,7 +57,6 @@ public class VersionUtils {
       throw new IllegalArgumentException("maxLengthToCompare cannot be less than 0");
     }
 
-
     if(BootStrapImpl.DEV_VERSION.equals(version1.trim())) return 0;
 
     String[] version1Parts = version1.split("\\.");

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 ad2d136..ff406b3 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
@@ -80,6 +80,7 @@ import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostHealthStatus;
 import org.apache.ambari.server.state.HostState;
@@ -2158,7 +2159,6 @@ public class TestHeartbeatHandler {
     hostAttributes.put("os_release_version", "6.3");
     clusters.getHost(DummyHostname1).setHostAttributes(hostAttributes);
 
-
     clusters.getHost(DummyHostname1).persist();
     clusters.addCluster(DummyCluster);
 
@@ -2166,6 +2166,7 @@ public class TestHeartbeatHandler {
     StackId stackId = new StackId(DummyStackId);
     cluster.setDesiredStackVersion(stackId);
     cluster.setCurrentStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     return cluster;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 08c18ca..26bf0b9 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
@@ -40,6 +40,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
 import org.apache.ambari.server.state.Host;
@@ -113,7 +114,9 @@ public class TestHeartbeatMonitor {
     clusters.getHost(hostname2).persist();
     clusters.addCluster(clusterName);
     Cluster cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
       add(hostname2);
@@ -196,7 +199,9 @@ public class TestHeartbeatMonitor {
     clusters.getHost(hostname2).persist();
     clusters.addCluster(clusterName);
     Cluster cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     Set<String> hostNames = new HashSet<String>() {{
       add(hostname1);
       add(hostname2);
@@ -299,7 +304,9 @@ public class TestHeartbeatMonitor {
     clusters.getHost(hostname1).persist();
     clusters.addCluster(clusterName);
     Cluster cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
@@ -407,7 +414,9 @@ public class TestHeartbeatMonitor {
 
     clusters.addCluster(clusterName);
     Cluster cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
@@ -523,7 +532,10 @@ public class TestHeartbeatMonitor {
     clusters.addCluster(clusterName);
 
     Cluster cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-2.0.7"));
+    StackId stackId = new StackId("HDP-2.0.7");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     Set<String> hostNames = new HashSet<String>(){{
       add(hostname1);
       add(hostname2);

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 a5b35e5..7a194a4 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
@@ -93,6 +93,7 @@ import org.apache.ambari.server.security.authorization.Users;
 import org.apache.ambari.server.serveraction.ServerAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
 import org.apache.ambari.server.state.ConfigHelper;
@@ -241,6 +242,11 @@ public class AmbariManagementControllerTest {
     clusters.deleteHost(hostname);
   }
 
+  /**
+   * Creates a Cluster object, along with its corresponding ClusterVersion based on the stack.
+   * @param clusterName Cluster name
+   * @throws AmbariException
+   */
   private void createCluster(String clusterName) throws AmbariException {
     ClusterRequest r = new ClusterRequest(null, clusterName, State.INSTALLED.name(), "HDP-0.1", null);
     controller.createCluster(r);
@@ -897,7 +903,9 @@ public class AmbariManagementControllerTest {
     }
 
     Cluster c1 = clusters.getCluster("c1");
-    c1.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    c1.setDesiredStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     Service s1 = serviceFactory.createNew(c1, "HDFS");
     Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
     c1.addService(s1);
@@ -977,7 +985,10 @@ public class AmbariManagementControllerTest {
     clusters.addCluster("c2");
 
     Cluster c1 = clusters.getCluster("c1");
-    c1.setDesiredStackVersion(new StackId("HDP-0.2"));
+    StackId stackId = new StackId("HDP-0.2");
+    c1.setDesiredStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     Service s1 = serviceFactory.createNew(c1, "HDFS");
     Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
     c1.addService(s1);
@@ -1241,15 +1252,21 @@ public class AmbariManagementControllerTest {
     Cluster foo = clusters.getCluster("foo");
     Cluster c1 = clusters.getCluster("c1");
     Cluster c2 = clusters.getCluster("c2");
+
     StackId stackId = new StackId("HDP-0.2");
     foo.setDesiredStackVersion(stackId);
     foo.setCurrentStackVersion(stackId);
+    foo.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     stackId = new StackId("HDP-0.2");
     c1.setDesiredStackVersion(stackId);
     c1.setCurrentStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     stackId = new StackId("HDP-0.2");
     c2.setDesiredStackVersion(stackId);
     c2.setCurrentStackVersion(stackId);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     try {
       set1.clear();
@@ -1453,7 +1470,10 @@ public class AmbariManagementControllerTest {
     }
 
     clusters.addCluster("foo");
-    clusters.getCluster("foo").setDesiredStackVersion(new StackId("HDP-0.1"));
+    Cluster c = clusters.getCluster("foo");
+    StackId stackId = new StackId("HDP-0.1");
+    c.setDesiredStackVersion(stackId);
+    c.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     HostResourceProviderTest.createHosts(controller, requests);
 
@@ -1471,7 +1491,11 @@ public class AmbariManagementControllerTest {
     clusters.addHost("h2");
     clusters.addHost("h3");
     clusters.addCluster("c1");
-    clusters.getCluster("c1").setDesiredStackVersion(new StackId("HDP-0.1"));
+    Cluster c = clusters.getCluster("c1");
+    StackId stackID = new StackId("HDP-0.1");
+    c.setDesiredStackVersion(stackID);
+    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
     setOsFamily(clusters.getHost("h2"), "redhat", "5.9");
     setOsFamily(clusters.getHost("h3"), "redhat", "5.9");
@@ -1501,7 +1525,7 @@ public class AmbariManagementControllerTest {
     Assert.assertEquals(0, clusters.getClustersForHost("h3").size());
 
     Assert.assertEquals(4, clusters.getHost("h2").getHostAttributes().size());
-    Assert.assertEquals(4, clusters.getHost("h3").getHostAttributes().size());
+    Assert.assertEquals(2, clusters.getHost("h3").getHostAttributes().size());
     Assert.assertEquals("val1",
         clusters.getHost("h2").getHostAttributes().get("attr1"));
     Assert.assertEquals("val2",
@@ -1825,7 +1849,9 @@ public class AmbariManagementControllerTest {
 
     Cluster c1 = clusters.getCluster("c1");
 
-    c1.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    c1.setDesiredStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     ClusterRequest r = new ClusterRequest(null, null, null, null);
     Set<ClusterResponse> resp = controller.getClusters(Collections.singleton(r));
@@ -7738,7 +7764,10 @@ public class AmbariManagementControllerTest {
     final String context = "Test invocation";
 
     clusters.addCluster(clusterName);
-    clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
+    Cluster c = clusters.getCluster(clusterName);
+    StackId stackID = new StackId("HDP-0.1");
+    c.setDesiredStackVersion(stackID);
+    c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     clusters.addHost(hostName1);
     setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
     clusters.getHost(hostName1).persist();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/test/java/org/apache/ambari/server/controller/RefreshYarnCapacitySchedulerReleaseConfigTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/RefreshYarnCapacitySchedulerReleaseConfigTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/RefreshYarnCapacitySchedulerReleaseConfigTest.java
index 6565219..02e5277 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/RefreshYarnCapacitySchedulerReleaseConfigTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/RefreshYarnCapacitySchedulerReleaseConfigTest.java
@@ -98,8 +98,9 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
     
     Assert.assertEquals(true, configHelper.isStaleConfigs(clusters.getCluster("c1").getService("YARN").getServiceComponent("RESOURCEMANAGER").getServiceComponentHost("c6401")));
   }
+
   @Test
-  public void testAllRequeiresRestart() throws AmbariException{
+  public void testAllRequiresRestart() throws AmbariException{
     createClusterFixture("HDP-2.0.7");
     Cluster cluster = clusters.getCluster("c1");
     
@@ -117,6 +118,7 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
     Assert.assertEquals(4, resps.size());
     
   }
+
   @Test
   public void testConfigInComponent() throws Exception {
     StackServiceRequest requestWithParams = new StackServiceRequest("HDP", "2.0.6", "YARN");
@@ -128,7 +130,8 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
       Assert.assertEquals(responseWithParams.getServiceName(), "YARN");
       Assert.assertTrue(responseWithParams.getConfigTypes().containsKey("capacity-scheduler"));
     }
-  }  
+  }
+
   @Test
   public void testConfigInComponentOverwrited() throws Exception {
     StackServiceRequest requestWithParams = new StackServiceRequest("HDP", "2.0.7", "YARN");
@@ -140,7 +143,8 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
       Assert.assertEquals(responseWithParams.getServiceName(), "YARN");
       Assert.assertTrue(responseWithParams.getConfigTypes().containsKey("capacity-scheduler"));
     }
-  }  
+  }
+
   private void createClusterFixture(String stackName) throws AmbariException {
     createCluster("c1", stackName);
     addHost("c6401","c1");
@@ -159,6 +163,7 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
     createServiceComponentHost("c1","YARN","NODEMANAGER","c6402", null);
     createServiceComponentHost("c1","YARN","YARN_CLIENT","c6402", null);
   }
+
   private void addHost(String hostname, String clusterName) throws AmbariException {
     clusters.addHost(hostname);
     setOsFamily(clusters.getHost(hostname), "redhat", "6.3");
@@ -167,6 +172,7 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
     if (null != clusterName)
       clusters.mapHostToCluster(hostname, clusterName);
   }
+
   private void setOsFamily(Host host, String osFamily, String osVersion) {
     Map<String, String> hostAttributes = new HashMap<String, String>();
     hostAttributes.put("os_family", osFamily);
@@ -231,7 +237,4 @@ public class RefreshYarnCapacitySchedulerReleaseConfigTest {
         put("hive-group", new HashMap<String,String>() {{ put("tag", "version1"); }});
       }}));
   }
-  
-  
-
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 85a32a6..58a2b23 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
@@ -45,6 +45,7 @@ import org.apache.ambari.server.controller.utilities.StreamProvider;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.StackId;
@@ -83,7 +84,9 @@ public class StackDefinedPropertyProviderTest {
     clusters.addCluster("c1");
 
     Cluster cluster = clusters.getCluster("c1");
-    cluster.setDesiredStackVersion(new StackId("HDP-2.0.5"));
+    StackId stackId = new StackId("HDP-2.0.5");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     clusters.addHost("h1");
     Host host = clusters.getHost("h1");

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 b23845c..87a0a48 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
@@ -41,6 +41,7 @@ import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.StackId;
@@ -83,7 +84,9 @@ public class UpgradeResourceProviderTest {
     clusters = injector.getInstance(Clusters.class);
     clusters.addCluster("c1");
     Cluster cluster = clusters.getCluster("c1");
-    cluster.setDesiredStackVersion(new StackId("HDP-2.1.1"));
+    StackId stackId = new StackId("HDP-2.1.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     clusters.addHost("h1");
     Host host = clusters.getHost("h1");

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 ec85cbd..f681481 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
@@ -29,6 +29,7 @@ import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
@@ -106,8 +107,10 @@ public class EventsTest {
     host.persist();
 
     m_cluster = m_clusters.getCluster(m_clusterName);
-    m_cluster.setDesiredStackVersion(new StackId("HDP", "2.0.6"));
     Assert.assertNotNull(m_cluster);
+    StackId stackId = new StackId("HDP", "2.0.6");
+    m_cluster.setDesiredStackVersion(stackId);
+    m_cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     m_clusters.mapHostToCluster(HOSTNAME, m_clusterName);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
index 79577e0..0d2d305 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
@@ -55,6 +55,7 @@ import org.apache.ambari.server.orm.entities.AlertNoticeEntity;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
@@ -685,7 +686,9 @@ public class AlertDispatchDAOTest {
     m_clusters.addCluster(clusterName);
 
     Cluster cluster = m_clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP", "2.0.6"));
+    StackId stackId = new StackId("HDP", "2.0.6");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     addHost();
     m_clusters.mapHostToCluster(HOSTNAME, cluster.getClusterName());

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
index 7634a11..a42dbe7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
@@ -58,6 +58,7 @@ import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
@@ -979,7 +980,9 @@ public class AlertsDAOTest {
     m_clusters.addCluster(clusterName);
 
     Cluster cluster = m_clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP", "2.0.6"));
+    StackId stackId = new StackId("HDP", "2.0.6");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     addHost();
     m_clusters.mapHostToCluster(HOSTNAME, cluster.getClusterName());

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ClusterVersionDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ClusterVersionDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ClusterVersionDAOTest.java
index f83fa95..4b29635 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ClusterVersionDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ClusterVersionDAOTest.java
@@ -149,23 +149,23 @@ public class ClusterVersionDAOTest {
     Assert.assertEquals(0, clusterVersionDAO.findByStackAndVersion("non existing", "non existing").size());
     Assert.assertEquals(1, clusterVersionDAO.findByStackAndVersion("HDP", "2.2.0.0-995").size());
   }
-
+  
   @Test
-  public void testFindByClusterAndStackAndVersion() {
+  public void testFindByCluster() {
     createRecordsUntilStep(1);
-    Assert.assertNull(clusterVersionDAO.findByClusterAndStackAndVersion(cluster.getClusterName(), "non existing", "non existing"));
-    Assert.assertNotNull(clusterVersionDAO.findByClusterAndStackAndVersion(cluster.getClusterName(), "HDP", "2.2.0.0-995"));
+    Assert.assertEquals(0, clusterVersionDAO.findByCluster("non existing").size());
+    Assert.assertEquals(1, clusterVersionDAO.findByCluster(cluster.getClusterName()).size());
   }
-
+  
   @Test
-  public void testFindByCluster() {
+  public void testFindByClusterAndStackAndVersion() {
     createRecordsUntilStep(1);
-    Assert.assertTrue(clusterVersionDAO.findByCluster("non existing").isEmpty());
-    Assert.assertNotNull(clusterVersionDAO.findByCluster(cluster.getClusterName()));
+    Assert.assertNull(clusterVersionDAO.findByClusterAndStackAndVersion(cluster.getClusterName(), "non existing", "non existing"));
+    Assert.assertNotNull(clusterVersionDAO.findByClusterAndStackAndVersion(cluster.getClusterName(), "HDP", "2.2.0.0-995"));
   }
-
+  
   /**
-   * At all times the cluster should have a cluster version whose state is {@link RepositoryVersionState#CURRENT}
+   * At all times the cluster should have a cluster version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}
    */
   @Test
   public void testFindByClusterAndStateCurrent() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
index 93cdc5d..44bdf94 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
@@ -186,7 +186,23 @@ public class HostVersionDAOTest {
   public void testFindAll() {
     Assert.assertEquals(3, hostVersionDAO.findAll().size());
   }
+  
+  /**
+   * Test the {@link HostVersionDAO#findByHost(String)} method.
+   */
+  @Test
+  public void testFindByHost() {
+    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host1").size());
+    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host2").size());
+    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host3").size());
 
+    addMoreVersions();
+
+    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host1").size());
+    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host2").size());
+    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host3").size());
+  }
+  
   /**
    * Test the {@link HostVersionDAO#findByClusterStackAndVersion(String, String, String)} method.
    */
@@ -219,23 +235,7 @@ public class HostVersionDAOTest {
   }
 
   /**
-   * Test the {@link HostVersionDAO#findByHost(String)} method.
-   */
-  @Test
-  public void testFindByHost() {
-    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host1").size());
-    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host2").size());
-    Assert.assertEquals(1, hostVersionDAO.findByHost("test_host3").size());
-
-    addMoreVersions();
-
-    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host1").size());
-    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host2").size());
-    Assert.assertEquals(3, hostVersionDAO.findByHost("test_host3").size());
-  }
-
-  /**
-   * Test the {@link HostVersionDAO#findByClusterHostAndState(String, String, org.apache.ambari.server.state.UpgradeState)} method.
+   * Test the {@link HostVersionDAO#findByClusterHostAndState(String, String, org.apache.ambari.server.state.RepositoryVersionState)} method.
    */
   @Test
   public void testFindByClusterHostAndState() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 605cd1b..652299c 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
@@ -39,7 +39,7 @@ import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntityPK;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntityPK;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostImpl;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -78,8 +78,11 @@ public class ServiceComponentTest {
     serviceName = "HDFS";
     clusters.addCluster(clusterName);
     cluster = clusters.getCluster(clusterName);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
     Assert.assertNotNull(cluster);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     Service s = serviceFactory.createNew(cluster, serviceName);
     cluster.addService(s);
     s.persist();

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 e8bdc14..9cdb9ad 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
@@ -58,6 +58,7 @@ import org.apache.ambari.server.orm.entities.HostStateEntity;
 import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntity;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
@@ -132,7 +133,9 @@ public class ClusterTest {
     host.setHostAttributes(hostAttributes);
     
     host.persist();
-    c1.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    c1.setDesiredStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     clusters.mapHostToCluster("h1", "c1");
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 f90c2c5..0d55792 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
@@ -48,6 +48,7 @@ import org.apache.ambari.server.orm.entities.ClusterStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntityPK;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntityPK;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
@@ -214,10 +215,17 @@ public class ClustersTest {
 
     clusters.addCluster(c1);
     clusters.addCluster(c2);
-    clusters.getCluster(c1).setDesiredStackVersion(new StackId("HDP-0.1"));
-    clusters.getCluster(c2).setDesiredStackVersion(new StackId("HDP-0.1"));
+
+    Cluster cluster1 = clusters.getCluster(c1);
+    Cluster cluster2 = clusters.getCluster(c2);
     Assert.assertNotNull(clusters.getCluster(c1));
     Assert.assertNotNull(clusters.getCluster(c2));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster1.setDesiredStackVersion(stackId);
+    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster2.setDesiredStackVersion(stackId);
+    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     try {
       clusters.mapHostToCluster(h1, c1);
       fail("Expected exception for invalid host");
@@ -293,8 +301,15 @@ public class ClustersTest {
     String h3 = "h3";
     clusters.addCluster(c1);
     clusters.addCluster(c2);
-    clusters.getCluster(c1).setDesiredStackVersion(new StackId("HDP-0.1"));
-    clusters.getCluster(c2).setDesiredStackVersion(new StackId("HDP-0.1"));
+    Cluster cluster1 = clusters.getCluster(c1);
+    Cluster cluster2 = clusters.getCluster(c2);
+    Assert.assertNotNull(clusters.getCluster(c1));
+    Assert.assertNotNull(clusters.getCluster(c2));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster1.setDesiredStackVersion(stackId);
+    cluster1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    cluster2.setDesiredStackVersion(stackId);
+    cluster2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     clusters.addHost(h1);
     clusters.addHost(h2);
     clusters.addHost(h3);
@@ -321,8 +336,10 @@ public class ClustersTest {
     clusters.addCluster(c1);
 
     Cluster cluster = clusters.getCluster(c1);
-    cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
-    cluster.setCurrentStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    cluster.setDesiredStackVersion(stackId);
+    cluster.setCurrentStackVersion(stackId);
+    cluster.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
 
     final Config config1 = injector.getInstance(ConfigFactory.class).createNew(cluster, "t1",
         new HashMap<String, String>() {{
@@ -411,11 +428,10 @@ public class ClustersTest {
     Assert.assertEquals(0, injector.getProvider(EntityManager.class).get().createQuery("SELECT config FROM ClusterConfigEntity config").getResultList().size());
     Assert.assertEquals(0, injector.getProvider(EntityManager.class).get().createQuery("SELECT state FROM ClusterStateEntity state").getResultList().size());
     Assert.assertEquals(0, injector.getProvider(EntityManager.class).get().createQuery("SELECT config FROM ClusterConfigMappingEntity config").getResultList().size());
-    
   }
+
   @Test
   public void testSetCurrentStackVersion() throws AmbariException {
-
     String c1 = "foo3";
 
     try

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 ae98694..ab158e9 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
@@ -43,6 +43,7 @@ import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostStateEntity;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
@@ -358,6 +359,8 @@ public class HostTest {
     
     clusters.addCluster("c1");
     Cluster c1 = clusters.getCluster("c1");
+    StackId stackId = new StackId("HDP-0.1");
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     Assert.assertEquals("c1", c1.getClusterName());
     Assert.assertEquals(1, c1.getClusterId());
     clusters.addHost("h1");
@@ -371,7 +374,7 @@ public class HostTest {
     host.setHostAttributes(hostAttributes);
     
     host.persist();
-    c1.setDesiredStackVersion(new StackId("HDP-0.1"));
+    c1.setDesiredStackVersion(stackId);
     clusters.mapHostToCluster("h1", "c1");
     
     ConfigFactory configFactory = injector.getInstance(ConfigFactory.class);
@@ -430,7 +433,9 @@ public class HostTest {
     host.setHostAttributes(hostAttributes);
     
     host.persist();
-    c1.setDesiredStackVersion(new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+    c1.setDesiredStackVersion(stackId);
     clusters.mapHostToCluster("h1", "c1");
 
     HostEntity entity = hostDAO.findByName("h1");

http://git-wip-us.apache.org/repos/asf/ambari/blob/c938c484/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 5410ee3..aa7aab5 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
@@ -36,6 +36,7 @@ import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntityPK;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigFactory;
@@ -101,8 +102,10 @@ public class ServiceComponentHostTest {
     clusters.addHost("h1");
     setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
     clusters.getHost("h1").persist();
-    clusters.getCluster("C1").setDesiredStackVersion(
-        new StackId("HDP-0.1"));
+    StackId stackId = new StackId("HDP-0.1");
+    Cluster c1 = clusters.getCluster("C1");
+    c1.setDesiredStackVersion(stackId);
+    c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     metaInfo.init();
     clusters.mapHostToCluster("h1","C1");
   }
@@ -704,8 +707,10 @@ public class ServiceComponentHostTest {
     clusters.addHost(hostName);
     setOsFamily(clusters.getHost(hostName), "redhat", "5.9");
     clusters.getHost(hostName).persist();
-    clusters.getCluster(clusterName).setDesiredStackVersion(
-        new StackId(stackVersion));
+    Cluster c2 = clusters.getCluster(clusterName);
+    StackId stackId = new StackId(stackVersion);
+    c2.setDesiredStackVersion(stackId);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
 
@@ -928,8 +933,11 @@ public class ServiceComponentHostTest {
     clusters.addHost(hostName);
     setOsFamily(clusters.getHost(hostName), "redhat", "5.9");
     clusters.getHost(hostName).persist();
-    clusters.getCluster(clusterName).setDesiredStackVersion(
-        new StackId(stackVersion));
+    Cluster c2 = clusters.getCluster(clusterName);
+    StackId stackId = new StackId(stackVersion);
+    c2.setDesiredStackVersion(stackId);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
+
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);
 
@@ -1059,8 +1067,10 @@ public class ServiceComponentHostTest {
     clusters.addHost(hostName);
     setOsFamily(clusters.getHost(hostName), "redhat", "5.9");
     clusters.getHost(hostName).persist();
-    clusters.getCluster(clusterName).setDesiredStackVersion(
-        new StackId(stackVersion));
+    Cluster c2 = clusters.getCluster(clusterName);
+    StackId stackId = new StackId(stackVersion);
+    c2.setDesiredStackVersion(stackId);
+    c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin", RepositoryVersionState.CURRENT);
     metaInfo.init();
     clusters.mapHostToCluster(hostName, clusterName);